forked from upstream-mirrors/quik
Merge pull request #674 from benwaffle/view-bindings-2
Finish migration to ViewBinding
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'realm-android' // Realm needs to be before Kotlin or the build will fail
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'realm-android' // Realm needs to be before Kotlin or the build will fail
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
@@ -107,10 +106,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
configurations {
|
||||
debug
|
||||
release
|
||||
|
||||
@@ -24,11 +24,13 @@ import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class QkActivity : AppCompatActivity() {
|
||||
@@ -36,6 +38,9 @@ abstract class QkActivity : AppCompatActivity() {
|
||||
|
||||
protected val menu: Subject<Menu> = BehaviorSubject.create()
|
||||
|
||||
protected val toolbar: Toolbar? get() = findViewById(R.id.toolbar)
|
||||
protected val toolbarTitle: TextView? get() = findViewById(R.id.toolbarTitle)
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -21,15 +21,14 @@ package dev.octoshrimpy.quik.common.base
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.bluelinelabs.conductor.archlifecycle.LifecycleController
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.*
|
||||
import kotlinx.android.synthetic.main.toolbar.view.*
|
||||
import dev.octoshrimpy.quik.R
|
||||
|
||||
abstract class QkController<ViewContract : QkViewContract<State>, State, Presenter : QkPresenter<ViewContract, State>> : LifecycleController(), LayoutContainer {
|
||||
abstract class QkController<VB : ViewBinding, ViewContract : QkViewContract<State>, State, Presenter : QkPresenter<ViewContract, State>> : LifecycleController() {
|
||||
|
||||
abstract var presenter: Presenter
|
||||
|
||||
@@ -39,16 +38,16 @@ abstract class QkController<ViewContract : QkViewContract<State>, State, Present
|
||||
protected val themedActivity: QkThemedActivity?
|
||||
get() = activity as? QkThemedActivity
|
||||
|
||||
override var containerView: View? = null
|
||||
private var _binding: VB? = null
|
||||
protected val binding: VB
|
||||
get() = requireNotNull(_binding)
|
||||
|
||||
@LayoutRes
|
||||
var layoutRes: Int = 0
|
||||
protected abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): VB
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
return inflater.inflate(layoutRes, container, false).also {
|
||||
containerView = it
|
||||
onViewCreated()
|
||||
}
|
||||
val vb = inflateBinding(inflater, container)
|
||||
_binding = vb
|
||||
return vb.root.also { onViewCreated() }
|
||||
}
|
||||
|
||||
open fun onViewCreated() {
|
||||
@@ -60,7 +59,7 @@ abstract class QkController<ViewContract : QkViewContract<State>, State, Present
|
||||
|
||||
fun setTitle(title: CharSequence?) {
|
||||
activity?.title = title
|
||||
view?.toolbarTitle?.text = title
|
||||
view?.findViewById<TextView>(R.id.toolbarTitle)?.text = title
|
||||
}
|
||||
|
||||
fun showBackButton(show: Boolean) {
|
||||
@@ -68,8 +67,8 @@ abstract class QkController<ViewContract : QkViewContract<State>, State, Present
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
containerView = null
|
||||
clearFindViewByIdCache()
|
||||
super.onDestroyView(view)
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
@@ -43,7 +43,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxkotlin.Observables
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -21,10 +21,7 @@ package dev.octoshrimpy.quik.common.base
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
|
||||
open class QkViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer {
|
||||
override val containerView: View = view
|
||||
}
|
||||
open class QkViewHolder(view: View) : RecyclerView.ViewHolder(view)
|
||||
|
||||
open class QkBindingViewHolder<T : ViewBinding>(val binding: T) : QkViewHolder(binding.root)
|
||||
|
||||
@@ -38,7 +38,11 @@ class PreferenceView @JvmOverloads constructor(
|
||||
|
||||
private var layout: PreferenceViewBinding
|
||||
|
||||
val titleTextView: TextView get() = layout.titleView
|
||||
val titleView: TextView get() = layout.titleView
|
||||
|
||||
val widget: View? get() = layout.widgetFrame.getChildAt(0)
|
||||
|
||||
val checkbox: QkSwitch? get() = widget as? QkSwitch
|
||||
|
||||
var title: String? = null
|
||||
set(value) {
|
||||
|
||||
@@ -69,6 +69,10 @@ class RadioPreferenceView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val radioButton get() = layout.radioButton
|
||||
val titleView get() = layout.titleView
|
||||
val summaryView get() = layout.summaryView
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
|
||||
@@ -22,7 +22,9 @@ import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@@ -41,14 +43,13 @@ import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import dev.octoshrimpy.quik.repository.BackupRepository
|
||||
import dev.octoshrimpy.quik.databinding.BackupControllerBinding
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.backup_controller.*
|
||||
import kotlinx.android.synthetic.main.preference_view.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class BackupController : QkController<BackupView, BackupState, BackupPresenter>(), BackupView {
|
||||
class BackupController : QkController<BackupControllerBinding, BackupView, BackupState, BackupPresenter>(), BackupView {
|
||||
|
||||
@Inject override lateinit var presenter: BackupPresenter
|
||||
|
||||
@@ -109,9 +110,11 @@ class BackupController : QkController<BackupView, BackupState, BackupPresenter>(
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
layoutRes = R.layout.backup_controller
|
||||
}
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): BackupControllerBinding =
|
||||
BackupControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onContextAvailable(context: Context) {
|
||||
// Init activity result contracts
|
||||
openDirectory = themedActivity!!
|
||||
@@ -136,57 +139,57 @@ class BackupController : QkController<BackupView, BackupState, BackupPresenter>(
|
||||
super.onViewCreated()
|
||||
|
||||
themedActivity?.colors?.theme()?.let { theme ->
|
||||
progressBar.indeterminateTintList = ColorStateList.valueOf(theme.theme)
|
||||
progressBar.progressTintList = ColorStateList.valueOf(theme.theme)
|
||||
fab.setBackgroundTint(theme.theme)
|
||||
fabIcon.setTint(theme.textPrimary)
|
||||
fabLabel.setTextColor(theme.textPrimary)
|
||||
binding.progressBar.indeterminateTintList = ColorStateList.valueOf(theme.theme)
|
||||
binding.progressBar.progressTintList = ColorStateList.valueOf(theme.theme)
|
||||
binding.fab.setBackgroundTint(theme.theme)
|
||||
binding.fabIcon.setTint(theme.textPrimary)
|
||||
binding.fabLabel.setTextColor(theme.textPrimary)
|
||||
}
|
||||
|
||||
// Make the list titles bold
|
||||
linearLayout.children
|
||||
.mapNotNull { it as? PreferenceView }
|
||||
.map { it.titleView }
|
||||
.forEach { it.setTypeface(it.typeface, Typeface.BOLD) }
|
||||
binding.linearLayout.children
|
||||
.mapNotNull { it as? PreferenceView }
|
||||
.map { it.titleView }
|
||||
.forEach { it.setTypeface(it.typeface, Typeface.BOLD) }
|
||||
}
|
||||
|
||||
override fun render(state: BackupState) {
|
||||
when {
|
||||
state.backupProgress.running -> {
|
||||
progressIcon.setImageResource(R.drawable.ic_file_upload_black_24dp)
|
||||
progressTitle.setText(R.string.backup_backing_up)
|
||||
progressSummary.text = state.backupProgress.getLabel(activity!!)
|
||||
progressSummary.isVisible = progressSummary.text.isNotEmpty()
|
||||
progressCancel.isVisible = false
|
||||
binding.progressIcon.setImageResource(R.drawable.ic_file_upload_black_24dp)
|
||||
binding.progressTitle.setText(R.string.backup_backing_up)
|
||||
binding.progressSummary.text = state.backupProgress.getLabel(activity!!)
|
||||
binding.progressSummary.isVisible = binding.progressSummary.text.isNotEmpty()
|
||||
binding.progressCancel.isVisible = false
|
||||
val running = (state.backupProgress as? BackupRepository.Progress.Running)
|
||||
progressBar.isVisible =
|
||||
binding.progressBar.isVisible =
|
||||
state.backupProgress.indeterminate || (running?.max ?: 0) > 0
|
||||
progressBar.isIndeterminate = state.backupProgress.indeterminate
|
||||
progressBar.max = running?.max ?: 0
|
||||
progressBar.progress = running?.count ?: 0
|
||||
progress.isVisible = true
|
||||
fab.isVisible = false
|
||||
binding.progressBar.isIndeterminate = state.backupProgress.indeterminate
|
||||
binding.progressBar.max = running?.max ?: 0
|
||||
binding.progressBar.progress = running?.count ?: 0
|
||||
binding.progress.isVisible = true
|
||||
binding.fab.isVisible = false
|
||||
}
|
||||
|
||||
state.restoreProgress.running -> {
|
||||
progressIcon.setImageResource(R.drawable.ic_file_download_black_24dp)
|
||||
progressTitle.setText(R.string.backup_restoring)
|
||||
progressSummary.text = state.restoreProgress.getLabel(activity!!)
|
||||
progressSummary.isVisible = progressSummary.text.isNotEmpty()
|
||||
progressCancel.isVisible = true
|
||||
binding.progressIcon.setImageResource(R.drawable.ic_file_download_black_24dp)
|
||||
binding.progressTitle.setText(R.string.backup_restoring)
|
||||
binding.progressSummary.text = state.restoreProgress.getLabel(activity!!)
|
||||
binding.progressSummary.isVisible = binding.progressSummary.text.isNotEmpty()
|
||||
binding.progressCancel.isVisible = true
|
||||
val running = (state.restoreProgress as? BackupRepository.Progress.Running)
|
||||
progressBar.isVisible =
|
||||
binding.progressBar.isVisible =
|
||||
state.restoreProgress.indeterminate || (running?.max ?: 0) > 0
|
||||
progressBar.isIndeterminate = state.restoreProgress.indeterminate
|
||||
progressBar.max = running?.max ?: 0
|
||||
progressBar.progress = running?.count ?: 0
|
||||
progress.isVisible = true
|
||||
fab.isVisible = false
|
||||
binding.progressBar.isIndeterminate = state.restoreProgress.indeterminate
|
||||
binding.progressBar.max = running?.max ?: 0
|
||||
binding.progressBar.progress = running?.count ?: 0
|
||||
binding.progress.isVisible = true
|
||||
binding.fab.isVisible = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
progress.isVisible = false
|
||||
fab.isVisible = true
|
||||
binding.progress.isVisible = false
|
||||
binding.fab.isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,20 +202,20 @@ class BackupController : QkController<BackupView, BackupState, BackupPresenter>(
|
||||
|
||||
stopRestoreDialog.setShowing(state.showStopRestoreDialog)
|
||||
|
||||
fabIcon.setImageResource(when (state.upgraded) {
|
||||
binding.fabIcon.setImageResource(when (state.upgraded) {
|
||||
true -> R.drawable.ic_file_upload_black_24dp
|
||||
false -> R.drawable.ic_star_black_24dp
|
||||
})
|
||||
|
||||
fabLabel.setText(when (state.upgraded) {
|
||||
binding.fabLabel.setText(when (state.upgraded) {
|
||||
true -> R.string.backup_now
|
||||
false -> R.string.title_qksms_plus
|
||||
})
|
||||
}
|
||||
|
||||
override fun setBackupLocationClicks(): Observable<*> = location.clicks()
|
||||
override fun setBackupLocationClicks(): Observable<*> = binding.location.clicks()
|
||||
|
||||
override fun restoreClicks(): Observable<*> = restore.clicks()
|
||||
override fun restoreClicks(): Observable<*> = binding.restore.clicks()
|
||||
|
||||
override fun locationRationaleConfirmClicks(): Observable<*> = selectFolderConfirmSubject
|
||||
|
||||
@@ -224,13 +227,13 @@ class BackupController : QkController<BackupView, BackupState, BackupPresenter>(
|
||||
|
||||
override fun confirmRestoreBackupCancelClicks(): Observable<*> = confirmRestoreCancelSubject
|
||||
|
||||
override fun stopRestoreClicks(): Observable<*> = progressCancel.clicks()
|
||||
override fun stopRestoreClicks(): Observable<*> = binding.progressCancel.clicks()
|
||||
|
||||
override fun stopRestoreConfirmed(): Observable<*> = stopRestoreConfirmSubject
|
||||
|
||||
override fun stopRestoreCancel(): Observable<*> = stopRestoreCancelSubject
|
||||
|
||||
override fun backupClicks(): Observable<*> = fab.clicks()
|
||||
override fun backupClicks(): Observable<*> = binding.fab.clicks()
|
||||
|
||||
override fun documentTreeSelected(): Observable<Uri> = documentTreeSelectedSubject
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import dev.octoshrimpy.quik.R
|
||||
@@ -31,17 +33,19 @@ import dev.octoshrimpy.quik.feature.blocking.messages.BlockedMessagesController
|
||||
import dev.octoshrimpy.quik.feature.blocking.numbers.BlockedNumbersController
|
||||
import dev.octoshrimpy.quik.feature.blocking.filters.MessageContentFiltersController
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import kotlinx.android.synthetic.main.blocking_controller.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
import dev.octoshrimpy.quik.databinding.BlockingControllerBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockingController : QkController<BlockingView, BlockingState, BlockingPresenter>(), BlockingView {
|
||||
class BlockingController : QkController<BlockingControllerBinding, BlockingView, BlockingState, BlockingPresenter>(), BlockingView {
|
||||
|
||||
override val blockingManagerIntent by lazy { blockingManager.clicks() }
|
||||
override val blockedNumbersIntent by lazy { blockedNumbers.clicks() }
|
||||
override val messageContentFiltersIntent by lazy { messageContentFilters.clicks() }
|
||||
override val blockedMessagesIntent by lazy { blockedMessages.clicks() }
|
||||
override val dropClickedIntent by lazy { drop.clicks() }
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): BlockingControllerBinding =
|
||||
BlockingControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
override val blockingManagerIntent get() = binding.blockingManager.clicks()
|
||||
override val blockedNumbersIntent get() = binding.blockedNumbers.clicks()
|
||||
override val messageContentFiltersIntent get() = binding.messageContentFilters.clicks()
|
||||
override val blockedMessagesIntent get() = binding.blockedMessages.clicks()
|
||||
override val dropClickedIntent get() = binding.drop.clicks()
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
@Inject override lateinit var presenter: BlockingPresenter
|
||||
@@ -49,12 +53,11 @@ class BlockingController : QkController<BlockingView, BlockingState, BlockingPre
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.blocking_controller
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
parent.postDelayed({ parent?.animateLayoutChanges = true }, 100)
|
||||
binding.parent.postDelayed({ binding.parent.animateLayoutChanges = true }, 100)
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -65,9 +68,9 @@ class BlockingController : QkController<BlockingView, BlockingState, BlockingPre
|
||||
}
|
||||
|
||||
override fun render(state: BlockingState) {
|
||||
blockingManager.summary = state.blockingManager
|
||||
drop.checkbox.isChecked = state.dropEnabled
|
||||
blockedMessages.isEnabled = !state.dropEnabled
|
||||
binding.blockingManager.summary = state.blockingManager
|
||||
binding.drop.checkbox?.isChecked = state.dropEnabled
|
||||
binding.blockedMessages.isEnabled = !state.dropEnabled
|
||||
}
|
||||
|
||||
override fun openBlockedNumbers() {
|
||||
|
||||
+24
-21
@@ -32,15 +32,14 @@ import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilterData
|
||||
import dev.octoshrimpy.quik.databinding.MessageContentFiltersControllerBinding
|
||||
import dev.octoshrimpy.quik.databinding.MessageContentFiltersAddDialogBinding
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.message_content_filters_add_dialog.view.*
|
||||
import kotlinx.android.synthetic.main.message_content_filters_controller.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageContentFiltersController : QkController<MessageContentFiltersView, MessageContentFiltersState,
|
||||
class MessageContentFiltersController : QkController<MessageContentFiltersControllerBinding, MessageContentFiltersView, MessageContentFiltersState,
|
||||
MessageContentFiltersPresenter>(), MessageContentFiltersView {
|
||||
|
||||
@Inject override lateinit var presenter: MessageContentFiltersPresenter
|
||||
@@ -52,9 +51,11 @@ class MessageContentFiltersController : QkController<MessageContentFiltersView,
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.message_content_filters_controller
|
||||
}
|
||||
|
||||
override fun inflateBinding(inflater: android.view.LayoutInflater, container: android.view.ViewGroup): MessageContentFiltersControllerBinding =
|
||||
MessageContentFiltersControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
presenter.bindIntents(this)
|
||||
@@ -64,10 +65,10 @@ class MessageContentFiltersController : QkController<MessageContentFiltersView,
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
add.setBackgroundTint(colors.theme().theme)
|
||||
add.setTint(colors.theme().textPrimary)
|
||||
adapter.emptyView = empty
|
||||
filters.adapter = adapter
|
||||
binding.add.setBackgroundTint(colors.theme().theme)
|
||||
binding.add.setTint(colors.theme().textPrimary)
|
||||
adapter.emptyView = binding.empty
|
||||
binding.filters.adapter = adapter
|
||||
}
|
||||
|
||||
override fun render(state: MessageContentFiltersState) {
|
||||
@@ -75,35 +76,37 @@ class MessageContentFiltersController : QkController<MessageContentFiltersView,
|
||||
}
|
||||
|
||||
override fun removeFilter(): Observable<Long> = adapter.removeMessageContentFilter
|
||||
override fun addFilter(): Observable<*> = add.clicks()
|
||||
override fun addFilter(): Observable<*> = binding.add.clicks()
|
||||
override fun saveFilter(): Observable<MessageContentFilterData> = saveFilterSubject
|
||||
|
||||
override fun showAddDialog() {
|
||||
val layout = LayoutInflater.from(activity).inflate(R.layout.message_content_filters_add_dialog, null)
|
||||
val layout = MessageContentFiltersAddDialogBinding.inflate(LayoutInflater.from(activity))
|
||||
|
||||
(0 until layout.add_dialog.childCount)
|
||||
.map { index -> layout.add_dialog.getChildAt(index) }
|
||||
(0 until layout.addDialog.childCount)
|
||||
.map { index -> layout.addDialog.getChildAt(index) }
|
||||
.mapNotNull { view -> view as? PreferenceView }
|
||||
.map { preference -> preference.clicks().map { preference } }
|
||||
.let { Observable.merge(it) }
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
it.checkbox.isChecked = !it.checkbox.isChecked
|
||||
layout.caseSensitivity.isEnabled = !layout.regexp.checkbox.isChecked
|
||||
.subscribe { pref ->
|
||||
pref.checkbox?.let { it.isChecked = !it.isChecked }
|
||||
val regexChecked = layout.regexp.checkbox?.isChecked ?: false
|
||||
layout.caseSensitivity.isEnabled = !regexChecked
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(activity!!)
|
||||
.setView(layout)
|
||||
.setView(layout.root)
|
||||
.setPositiveButton(R.string.message_content_filters_dialog_create) { _, _ ->
|
||||
var text = layout.input.text.toString();
|
||||
if (!text.isBlank()) {
|
||||
if (!layout.regexp.checkbox.isChecked) text = text.trim()
|
||||
val regexChecked = layout.regexp.checkbox?.isChecked ?: false
|
||||
if (!regexChecked) text = text.trim()
|
||||
saveFilterSubject.onNext(
|
||||
MessageContentFilterData(
|
||||
text,
|
||||
layout.caseSensitivity.checkbox.isChecked && !layout.regexp.checkbox.isChecked,
|
||||
layout.regexp.checkbox.isChecked,
|
||||
layout.contacts.checkbox.isChecked
|
||||
(layout.caseSensitivity.checkbox?.isChecked == true) && !regexChecked,
|
||||
regexChecked,
|
||||
layout.contacts.checkbox?.isChecked == true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+27
-24
@@ -3,25 +3,29 @@ package dev.octoshrimpy.quik.feature.blocking.manager
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isInvisible
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
import dev.octoshrimpy.quik.databinding.BlockingManagerControllerBinding
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import kotlinx.android.synthetic.main.blocking_manager_controller.*
|
||||
import kotlinx.android.synthetic.main.blocking_manager_list_option.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockingManagerController : QkController<BlockingManagerView, BlockingManagerState, BlockingManagerPresenter>(),
|
||||
class BlockingManagerController : QkController<BlockingManagerControllerBinding, BlockingManagerView, BlockingManagerState, BlockingManagerPresenter>(),
|
||||
BlockingManagerView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): BlockingManagerControllerBinding =
|
||||
BlockingManagerControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
@Inject override lateinit var presenter: BlockingManagerPresenter
|
||||
|
||||
@@ -30,7 +34,6 @@ class BlockingManagerController : QkController<BlockingManagerView, BlockingMana
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.blocking_manager_controller
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -46,10 +49,10 @@ class BlockingManagerController : QkController<BlockingManagerView, BlockingMana
|
||||
val textTertiary = view.context.resolveThemeColor(android.R.attr.textColorTertiary)
|
||||
val imageTintList = ColorStateList(states, intArrayOf(colors.theme().theme, textTertiary))
|
||||
|
||||
qksms.action.imageTintList = imageTintList
|
||||
callBlocker.action.imageTintList = imageTintList
|
||||
callControl.action.imageTintList = imageTintList
|
||||
shouldIAnswer.action.imageTintList = imageTintList
|
||||
binding.qksms.actionView.imageTintList = imageTintList
|
||||
binding.callBlocker.actionView.imageTintList = imageTintList
|
||||
binding.callControl.actionView.imageTintList = imageTintList
|
||||
binding.shouldIAnswer.actionView.imageTintList = imageTintList
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
@@ -57,23 +60,23 @@ class BlockingManagerController : QkController<BlockingManagerView, BlockingMana
|
||||
}
|
||||
|
||||
override fun render(state: BlockingManagerState) {
|
||||
qksms.action.setImageResource(getActionIcon(true))
|
||||
qksms.action.isActivated = true
|
||||
qksms.action.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_QKSMS
|
||||
binding.qksms.actionView.setImageResource(getActionIcon(true))
|
||||
binding.qksms.actionView.isActivated = true
|
||||
binding.qksms.actionView.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_QKSMS
|
||||
|
||||
callBlocker.action.setImageResource(getActionIcon(state.callBlockerInstalled))
|
||||
callBlocker.action.isActivated = state.callBlockerInstalled
|
||||
callBlocker.action.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_CB
|
||||
binding.callBlocker.actionView.setImageResource(getActionIcon(state.callBlockerInstalled))
|
||||
binding.callBlocker.actionView.isActivated = state.callBlockerInstalled
|
||||
binding.callBlocker.actionView.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_CB
|
||||
&& state.callBlockerInstalled
|
||||
|
||||
callControl.action.setImageResource(getActionIcon(state.callControlInstalled))
|
||||
callControl.action.isActivated = state.callControlInstalled
|
||||
callControl.action.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_CC
|
||||
binding.callControl.actionView.setImageResource(getActionIcon(state.callControlInstalled))
|
||||
binding.callControl.actionView.isActivated = state.callControlInstalled
|
||||
binding.callControl.actionView.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_CC
|
||||
&& state.callControlInstalled
|
||||
|
||||
shouldIAnswer.action.setImageResource(getActionIcon(state.siaInstalled))
|
||||
shouldIAnswer.action.isActivated = state.siaInstalled
|
||||
shouldIAnswer.action.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_SIA
|
||||
binding.shouldIAnswer.actionView.setImageResource(getActionIcon(state.siaInstalled))
|
||||
binding.shouldIAnswer.actionView.isActivated = state.siaInstalled
|
||||
binding.shouldIAnswer.actionView.isInvisible = state.blockingManager != Preferences.BLOCKING_MANAGER_SIA
|
||||
&& state.siaInstalled
|
||||
}
|
||||
|
||||
@@ -83,10 +86,10 @@ class BlockingManagerController : QkController<BlockingManagerView, BlockingMana
|
||||
}
|
||||
|
||||
override fun activityResumed(): Observable<*> = activityResumedSubject
|
||||
override fun qksmsClicked(): Observable<*> = qksms.clicks()
|
||||
override fun callBlockerClicked(): Observable<*> = callBlocker.clicks()
|
||||
override fun callControlClicked(): Observable<*> = callControl.clicks()
|
||||
override fun siaClicked(): Observable<*> = shouldIAnswer.clicks()
|
||||
override fun qksmsClicked(): Observable<*> = binding.qksms.clicks()
|
||||
override fun callBlockerClicked(): Observable<*> = binding.callBlocker.clicks()
|
||||
override fun callControlClicked(): Observable<*> = binding.callControl.clicks()
|
||||
override fun siaClicked(): Observable<*> = binding.shouldIAnswer.clicks()
|
||||
|
||||
override fun showCopyDialog(manager: String): Single<Boolean> = Single.create { emitter ->
|
||||
AlertDialog.Builder(activity)
|
||||
|
||||
+2
@@ -39,6 +39,8 @@ class BlockingManagerPreferenceView @JvmOverloads constructor(
|
||||
private val binding: BlockingManagerPreferenceViewBinding =
|
||||
BlockingManagerPreferenceViewBinding.inflate(LayoutInflater.from(context), this)
|
||||
|
||||
val actionView: ImageView get() = binding.widgetFrame.findViewById(R.id.action)
|
||||
|
||||
var icon: Drawable? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
+24
-24
@@ -24,69 +24,69 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkBindingViewHolder
|
||||
import dev.octoshrimpy.quik.common.base.QkRealmAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.DateFormatter
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
import dev.octoshrimpy.quik.databinding.BlockedListItemBinding
|
||||
import dev.octoshrimpy.quik.model.Conversation
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import kotlinx.android.synthetic.main.blocked_list_item.*
|
||||
import kotlinx.android.synthetic.main.blocked_list_item.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockedMessagesAdapter @Inject constructor(
|
||||
private val context: Context,
|
||||
private val dateFormatter: DateFormatter
|
||||
) : QkRealmAdapter<Conversation, QkViewHolder>() {
|
||||
) : QkRealmAdapter<Conversation, QkBindingViewHolder<BlockedListItemBinding>>() {
|
||||
|
||||
val clicks: PublishSubject<Long> = PublishSubject.create()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.blocked_list_item, parent, false)
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkBindingViewHolder<BlockedListItemBinding> {
|
||||
val binding = BlockedListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
|
||||
if (viewType == 0) {
|
||||
view.title.setTypeface(view.title.typeface, Typeface.BOLD)
|
||||
view.date.setTypeface(view.date.typeface, Typeface.BOLD)
|
||||
view.date.setTextColor(view.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
binding.title.setTypeface(binding.title.typeface, Typeface.BOLD)
|
||||
binding.date.setTypeface(binding.date.typeface, Typeface.BOLD)
|
||||
binding.date.setTextColor(binding.root.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
}
|
||||
|
||||
return QkViewHolder(view).apply {
|
||||
view.setOnClickListener {
|
||||
return QkBindingViewHolder(binding).apply {
|
||||
binding.root.setOnClickListener {
|
||||
val conversation = getItem(adapterPosition) ?: return@setOnClickListener
|
||||
when (toggleSelection(conversation.id, false)) {
|
||||
true -> view.isActivated = isSelected(conversation.id)
|
||||
true -> binding.root.isActivated = isSelected(conversation.id)
|
||||
false -> clicks.onNext(conversation.id)
|
||||
}
|
||||
}
|
||||
view.setOnLongClickListener {
|
||||
binding.root.setOnLongClickListener {
|
||||
val conversation = getItem(adapterPosition) ?: return@setOnLongClickListener true
|
||||
toggleSelection(conversation.id)
|
||||
view.isActivated = isSelected(conversation.id)
|
||||
binding.root.isActivated = isSelected(conversation.id)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: QkBindingViewHolder<BlockedListItemBinding>, position: Int) {
|
||||
val conversation = getItem(position) ?: return
|
||||
val binding = holder.binding
|
||||
|
||||
holder.containerView.isActivated = isSelected(conversation.id)
|
||||
holder.itemView.isActivated = isSelected(conversation.id)
|
||||
|
||||
holder.avatars.recipients = conversation.recipients
|
||||
holder.title.collapseEnabled = conversation.recipients.size > 1
|
||||
holder.title.text = conversation.getTitle()
|
||||
holder.date.text = dateFormatter.getConversationTimestamp(conversation.date)
|
||||
binding.avatars.recipients = conversation.recipients
|
||||
binding.title.collapseEnabled = conversation.recipients.size > 1
|
||||
binding.title.text = conversation.getTitle()
|
||||
binding.date.text = dateFormatter.getConversationTimestamp(conversation.date)
|
||||
|
||||
holder.blocker.text = when (conversation.blockingClient) {
|
||||
binding.blocker.text = when (conversation.blockingClient) {
|
||||
Preferences.BLOCKING_MANAGER_CC -> context.getString(R.string.blocking_manager_call_control_title)
|
||||
Preferences.BLOCKING_MANAGER_SIA -> context.getString(R.string.blocking_manager_sia_title)
|
||||
else -> null
|
||||
}
|
||||
|
||||
holder.reason.text = conversation.blockReason
|
||||
holder.blocker.isVisible = holder.blocker.text.isNotEmpty()
|
||||
holder.reason.isVisible = holder.blocker.text.isNotEmpty()
|
||||
binding.reason.text = conversation.blockReason
|
||||
binding.blocker.isVisible = binding.blocker.text.isNotEmpty()
|
||||
binding.reason.isVisible = binding.blocker.text.isNotEmpty()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
|
||||
+13
-8
@@ -19,25 +19,30 @@
|
||||
package dev.octoshrimpy.quik.feature.blocking.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.databinding.BlockedMessagesControllerBinding
|
||||
import dev.octoshrimpy.quik.feature.blocking.BlockingDialog
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.blocked_messages_controller.*
|
||||
import kotlinx.android.synthetic.main.container_activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockedMessagesController : QkController<BlockedMessagesView, BlockedMessagesState, BlockedMessagesPresenter>(),
|
||||
class BlockedMessagesController : QkController<BlockedMessagesControllerBinding, BlockedMessagesView, BlockedMessagesState, BlockedMessagesPresenter>(),
|
||||
BlockedMessagesView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): BlockedMessagesControllerBinding =
|
||||
BlockedMessagesControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
override val menuReadyIntent: Subject<Unit> = PublishSubject.create()
|
||||
override val optionsItemIntent: Subject<Int> = PublishSubject.create()
|
||||
override val conversationClicks by lazy { blockedMessagesAdapter.clicks }
|
||||
@@ -54,13 +59,12 @@ class BlockedMessagesController : QkController<BlockedMessagesView, BlockedMessa
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.blocked_messages_controller
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
blockedMessagesAdapter.emptyView = empty
|
||||
conversations.adapter = blockedMessagesAdapter
|
||||
blockedMessagesAdapter.emptyView = binding.empty
|
||||
binding.conversations.adapter = blockedMessagesAdapter
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -90,8 +94,9 @@ class BlockedMessagesController : QkController<BlockedMessagesView, BlockedMessa
|
||||
override fun render(state: BlockedMessagesState) {
|
||||
blockedMessagesAdapter.updateData(state.data)
|
||||
|
||||
themedActivity?.toolbar?.menu?.findItem(R.id.block)?.isVisible = state.selected > 0
|
||||
themedActivity?.toolbar?.menu?.findItem(R.id.delete)?.isVisible = state.selected > 0
|
||||
val toolbarMenu = themedActivity?.findViewById<Toolbar>(R.id.toolbar)?.menu
|
||||
toolbarMenu?.findItem(R.id.block)?.isVisible = state.selected > 0
|
||||
toolbarMenu?.findItem(R.id.delete)?.isVisible = state.selected > 0
|
||||
|
||||
setTitle(when (state.selected) {
|
||||
0 -> context.getString(R.string.blocked_messages_title)
|
||||
|
||||
+14
-11
@@ -20,6 +20,7 @@ package dev.octoshrimpy.quik.feature.blocking.numbers
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import dev.octoshrimpy.quik.R
|
||||
@@ -32,11 +33,11 @@ import dev.octoshrimpy.quik.util.PhoneNumberUtils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.blocked_numbers_add_dialog.view.*
|
||||
import kotlinx.android.synthetic.main.blocked_numbers_controller.*
|
||||
import dev.octoshrimpy.quik.databinding.BlockedNumbersControllerBinding
|
||||
import dev.octoshrimpy.quik.databinding.BlockedNumbersAddDialogBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockedNumbersController : QkController<BlockedNumbersView, BlockedNumbersState, BlockedNumbersPresenter>(),
|
||||
class BlockedNumbersController : QkController<BlockedNumbersControllerBinding, BlockedNumbersView, BlockedNumbersState, BlockedNumbersPresenter>(),
|
||||
BlockedNumbersView {
|
||||
|
||||
@Inject override lateinit var presenter: BlockedNumbersPresenter
|
||||
@@ -49,9 +50,11 @@ class BlockedNumbersController : QkController<BlockedNumbersView, BlockedNumbers
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.blocked_numbers_controller
|
||||
}
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): BlockedNumbersControllerBinding =
|
||||
BlockedNumbersControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
presenter.bindIntents(this)
|
||||
@@ -61,10 +64,10 @@ class BlockedNumbersController : QkController<BlockedNumbersView, BlockedNumbers
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
add.setBackgroundTint(colors.theme().theme)
|
||||
add.setTint(colors.theme().textPrimary)
|
||||
adapter.emptyView = empty
|
||||
numbers.adapter = adapter
|
||||
binding.add.setBackgroundTint(colors.theme().theme)
|
||||
binding.add.setTint(colors.theme().textPrimary)
|
||||
adapter.emptyView = binding.empty
|
||||
binding.numbers.adapter = adapter
|
||||
}
|
||||
|
||||
override fun render(state: BlockedNumbersState) {
|
||||
@@ -72,14 +75,14 @@ class BlockedNumbersController : QkController<BlockedNumbersView, BlockedNumbers
|
||||
}
|
||||
|
||||
override fun unblockAddress(): Observable<Long> = adapter.unblockAddress
|
||||
override fun addAddress(): Observable<*> = add.clicks()
|
||||
override fun addAddress(): Observable<*> = binding.add.clicks()
|
||||
override fun saveAddress(): Observable<String> = saveAddressSubject
|
||||
|
||||
override fun showAddDialog() {
|
||||
val layout = LayoutInflater.from(activity).inflate(R.layout.blocked_numbers_add_dialog, null)
|
||||
val layout = BlockedNumbersAddDialogBinding.inflate(LayoutInflater.from(activity))
|
||||
val textWatcher = BlockedNumberTextWatcher(layout.input, phoneNumberUtils)
|
||||
val dialog = AlertDialog.Builder(activity!!)
|
||||
.setView(layout)
|
||||
.setView(layout.root)
|
||||
.setPositiveButton(R.string.blocked_numbers_dialog_block) { _, _ ->
|
||||
saveAddressSubject.onNext(layout.input.text.toString())
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.util.extensions.showKeyboard
|
||||
import dev.octoshrimpy.quik.common.widget.MicInputCloudView
|
||||
import dev.octoshrimpy.quik.common.widget.QkEditText
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.feature.compose.editing.ChipsAdapter
|
||||
import dev.octoshrimpy.quik.feature.contacts.ContactsActivity
|
||||
@@ -85,60 +84,9 @@ import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import dev.octoshrimpy.quik.databinding.ComposeActivityBinding
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.compose_activity.attach
|
||||
import kotlinx.android.synthetic.main.compose_activity.attachAFileIcon
|
||||
import kotlinx.android.synthetic.main.compose_activity.attachAFileLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.attachAnAudioMessageIcon
|
||||
import kotlinx.android.synthetic.main.compose_activity.attachAnAudioMessageLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.attaching
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgAbort
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgAttach
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgBackground
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgBluetooth
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgDuration
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgPlayerBackground
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgPlayerPlayPause
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgPlayerSeekBar
|
||||
import kotlinx.android.synthetic.main.compose_activity.audioMsgRecord
|
||||
import kotlinx.android.synthetic.main.compose_activity.camera
|
||||
import kotlinx.android.synthetic.main.compose_activity.cameraLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.chips
|
||||
import kotlinx.android.synthetic.main.compose_activity.composeBar
|
||||
import kotlinx.android.synthetic.main.compose_activity.contact
|
||||
import kotlinx.android.synthetic.main.compose_activity.contactLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.contentView
|
||||
import kotlinx.android.synthetic.main.compose_activity.counter
|
||||
import kotlinx.android.synthetic.main.compose_activity.gallery
|
||||
import kotlinx.android.synthetic.main.compose_activity.galleryLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.loading
|
||||
import kotlinx.android.synthetic.main.compose_activity.message
|
||||
import kotlinx.android.synthetic.main.compose_activity.messageAttachments
|
||||
import kotlinx.android.synthetic.main.compose_activity.messageList
|
||||
import kotlinx.android.synthetic.main.compose_activity.messagesEmpty
|
||||
import kotlinx.android.synthetic.main.compose_activity.noValidRecipients
|
||||
import kotlinx.android.synthetic.main.compose_activity.recordAudioMsg
|
||||
import kotlinx.android.synthetic.main.compose_activity.schedule
|
||||
import kotlinx.android.synthetic.main.compose_activity.scheduleLabel
|
||||
import kotlinx.android.synthetic.main.compose_activity.scheduledCancel
|
||||
import kotlinx.android.synthetic.main.compose_activity.scheduledGroup
|
||||
import kotlinx.android.synthetic.main.compose_activity.scheduledTime
|
||||
import kotlinx.android.synthetic.main.compose_activity.scheduledSend
|
||||
import kotlinx.android.synthetic.main.compose_activity.send
|
||||
import kotlinx.android.synthetic.main.compose_activity.sendAsGroup
|
||||
import kotlinx.android.synthetic.main.compose_activity.sendAsGroupBackground
|
||||
import kotlinx.android.synthetic.main.compose_activity.sendAsGroupSummary
|
||||
import kotlinx.android.synthetic.main.compose_activity.sendAsGroupSwitch
|
||||
import kotlinx.android.synthetic.main.compose_activity.shadeBackground
|
||||
import kotlinx.android.synthetic.main.compose_activity.sim
|
||||
import kotlinx.android.synthetic.main.compose_activity.simIndex
|
||||
import kotlinx.android.synthetic.main.compose_activity.speechToTextFrame
|
||||
import kotlinx.android.synthetic.main.compose_activity.speechToTextIcon
|
||||
import kotlinx.android.synthetic.main.compose_activity.speechToTextIconBorder
|
||||
import kotlinx.android.synthetic.main.compose_activity.toolbarSubtitle
|
||||
import kotlinx.android.synthetic.main.compose_activity.toolbarTitle
|
||||
import kotlinx.android.synthetic.main.main_activity.toolbar
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
@@ -156,6 +104,8 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
@Inject lateinit var navigator: Navigator
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
private lateinit var binding: ComposeActivityBinding
|
||||
|
||||
override val activityVisibleIntent: Subject<Boolean> = PublishSubject.create()
|
||||
override val chipsSelectedIntent: Subject<HashMap<String, String?>> = PublishSubject.create()
|
||||
override val chipDeletedIntent: Subject<Recipient> by lazy { chipsAdapter.chipDeleted }
|
||||
@@ -163,7 +113,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
override val optionsItemIntent: Subject<Int> = PublishSubject.create()
|
||||
override val contextItemIntent: Subject<MenuItem> = PublishSubject.create()
|
||||
override val scheduleAction: Subject<Boolean> = PublishSubject.create()
|
||||
override val sendAsGroupIntent by lazy { sendAsGroupBackground.clicks() }
|
||||
override val sendAsGroupIntent by lazy { binding.sendAsGroupBackground.clicks() }
|
||||
override val messagePartClickIntent: Subject<Long> by lazy { messageAdapter.partClicks }
|
||||
override val messagePartContextMenuRegistrar: Subject<View> by lazy { messageAdapter.partContextMenuRegistrar }
|
||||
override val messagesSelectedIntent by lazy { messageAdapter.selectionChanges }
|
||||
@@ -171,36 +121,36 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
override val sendDelayedNowIntent: Subject<Long> by lazy { messageAdapter.sendNowClicks }
|
||||
override val resendIntent: Subject<Long> by lazy { messageAdapter.resendClicks }
|
||||
override val attachmentDeletedIntent: Subject<Attachment> by lazy { composeAttachmentAdapter.attachmentDeleted }
|
||||
override val textChangedIntent by lazy { message.textChanges() }
|
||||
override val attachIntent: Observable<Unit> by lazy { Observable.merge(attach.clicks(), shadeBackground.clicks()) }
|
||||
override val cameraIntent: Observable<Unit> by lazy { Observable.merge(camera.clicks(), cameraLabel.clicks()) }
|
||||
override val attachImageFileIntent: Observable<Unit> by lazy { Observable.merge(gallery.clicks(), galleryLabel.clicks()) }
|
||||
override val attachAnyFileIntent: Observable<Unit> by lazy { Observable.merge(attachAFileIcon.clicks(), attachAFileLabel.clicks()) }
|
||||
override val scheduleIntent: Observable<Unit> by lazy { Observable.merge(schedule.clicks(), scheduleLabel.clicks()) }
|
||||
override val attachContactIntent: Observable<Unit> by lazy { Observable.merge(contact.clicks(), contactLabel.clicks()) }
|
||||
override val textChangedIntent by lazy { binding.message.textChanges() }
|
||||
override val attachIntent: Observable<Unit> by lazy { Observable.merge(binding.attach.clicks(), binding.shadeBackground.clicks()) }
|
||||
override val cameraIntent: Observable<Unit> by lazy { Observable.merge(binding.camera.clicks(), binding.cameraLabel.clicks()) }
|
||||
override val attachImageFileIntent: Observable<Unit> by lazy { Observable.merge(binding.gallery.clicks(), binding.galleryLabel.clicks()) }
|
||||
override val attachAnyFileIntent: Observable<Unit> by lazy { Observable.merge(binding.attachAFileIcon.clicks(), binding.attachAFileLabel.clicks()) }
|
||||
override val scheduleIntent: Observable<Unit> by lazy { Observable.merge(binding.schedule.clicks(), binding.scheduleLabel.clicks()) }
|
||||
override val attachContactIntent: Observable<Unit> by lazy { Observable.merge(binding.contact.clicks(), binding.contactLabel.clicks()) }
|
||||
override val attachAnyFileSelectedIntent: Subject<Uri> = PublishSubject.create()
|
||||
override val contactSelectedIntent: Subject<Uri> = PublishSubject.create()
|
||||
override val inputContentIntent by lazy { message.inputContentSelected }
|
||||
override val inputContentIntent by lazy { binding.message.inputContentSelected }
|
||||
override val scheduleSelectedIntent: Subject<Long> = PublishSubject.create()
|
||||
override val changeSimIntent by lazy { sim.clicks() }
|
||||
override val scheduleCancelIntent by lazy { scheduledCancel.clicks() }
|
||||
override val sendIntent by lazy { Observable.merge(send.clicks(), scheduledSend.clicks()) }
|
||||
override val changeSimIntent by lazy { binding.sim.clicks() }
|
||||
override val scheduleCancelIntent by lazy { binding.scheduledCancel.clicks() }
|
||||
override val sendIntent by lazy { Observable.merge(binding.send.clicks(), binding.scheduledSend.clicks()) }
|
||||
override val viewQksmsPlusIntent: Subject<Unit> = PublishSubject.create()
|
||||
override val backPressedIntent: Subject<Unit> = PublishSubject.create()
|
||||
override val confirmDeleteIntent: Subject<List<Long>> = PublishSubject.create()
|
||||
override val clearCurrentMessageIntent: Subject<Boolean> = PublishSubject.create()
|
||||
override val messageLinkAskIntent: Subject<Uri> by lazy { messageAdapter.messageLinkClicks }
|
||||
override val reactionClickIntent: Subject<Long> by lazy { messageAdapter.reactionClicks }
|
||||
override val speechRecogniserIntent by lazy { speechToTextIcon.clicks() }
|
||||
override val shadeIntent by lazy { shadeBackground.clicks() }
|
||||
override val speechRecogniserIntent by lazy { binding.speechToTextIcon.clicks() }
|
||||
override val shadeIntent by lazy { binding.shadeBackground.clicks() }
|
||||
override val recordAudioStartStopRecording: Subject<Boolean> = PublishSubject.create()
|
||||
override val recordAnAudioMessage: Observable<Unit> by lazy {
|
||||
Observable.merge(recordAudioMsg.clicks(),
|
||||
attachAnAudioMessageIcon.clicks(),
|
||||
attachAnAudioMessageLabel.clicks())
|
||||
Observable.merge(binding.recordAudioMsg.clicks(),
|
||||
binding.attachAnAudioMessageIcon.clicks(),
|
||||
binding.attachAnAudioMessageLabel.clicks())
|
||||
}
|
||||
override val recordAudioAbort by lazy { audioMsgAbort.clicks() }
|
||||
override val recordAudioAttach by lazy { audioMsgAttach.clicks() }
|
||||
override val recordAudioAbort by lazy { binding.audioMsgAbort.clicks() }
|
||||
override val recordAudioAttach by lazy { binding.audioMsgAttach.clicks() }
|
||||
override val recordAudioPlayerPlayPause: Subject<QkMediaPlayer.PlayingState> = PublishSubject.create()
|
||||
override val recordAudioPlayerConfigUI: Subject<QkMediaPlayer.PlayingState> = PublishSubject.create()
|
||||
override val recordAudioPlayerVisible: Subject<Boolean> = PublishSubject.create()
|
||||
@@ -228,56 +178,57 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.compose_activity)
|
||||
binding = ComposeActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
showBackButton(true)
|
||||
viewModel.bindView(this)
|
||||
|
||||
contentView.layoutTransition = LayoutTransition().apply {
|
||||
binding.contentView.layoutTransition = LayoutTransition().apply {
|
||||
disableTransitionType(LayoutTransition.CHANGING)
|
||||
}
|
||||
chipsAdapter.view = chips
|
||||
chipsAdapter.view = binding.chips
|
||||
|
||||
chips.itemAnimator = null
|
||||
chips.layoutManager = FlexboxLayoutManager(this)
|
||||
binding.chips.itemAnimator = null
|
||||
binding.chips.layoutManager = FlexboxLayoutManager(this)
|
||||
|
||||
messageAdapter.autoScrollToStart(messageList)
|
||||
messageAdapter.emptyView = messagesEmpty
|
||||
messageAdapter.autoScrollToStart(binding.messageList)
|
||||
messageAdapter.emptyView = binding.messagesEmpty
|
||||
|
||||
messageList.setHasFixedSize(true)
|
||||
messageList.adapter = messageAdapter
|
||||
binding.messageList.setHasFixedSize(true)
|
||||
binding.messageList.adapter = messageAdapter
|
||||
|
||||
messageAttachments.adapter = composeAttachmentAdapter
|
||||
binding.messageAttachments.adapter = composeAttachmentAdapter
|
||||
|
||||
message.supportsInputContent = true
|
||||
binding.message.supportsInputContent = true
|
||||
|
||||
theme
|
||||
.doOnNext {
|
||||
loading.setTint(it.theme)
|
||||
binding.loading.setTint(it.theme)
|
||||
|
||||
// entire attach menu
|
||||
attach.setBackgroundTint(it.theme); attach.setTint(it.textPrimary)
|
||||
contact.setBackgroundTint(it.theme); contact.setTint(it.textPrimary)
|
||||
contactLabel.setBackgroundTint(it.theme); contactLabel.setTint(it.textPrimary)
|
||||
schedule.setBackgroundTint(it.theme); schedule.setTint(it.textPrimary)
|
||||
scheduleLabel.setBackgroundTint(it.theme); scheduleLabel.setTint(it.textPrimary)
|
||||
attachAFileIcon.setBackgroundTint(it.theme); attachAFileIcon.setTint(it.textPrimary)
|
||||
attachAFileLabel.setBackgroundTint(it.theme); attachAFileLabel.setTint(it.textPrimary)
|
||||
attachAnAudioMessageIcon.setBackgroundTint(it.theme); attachAnAudioMessageIcon.setTint(it.textPrimary)
|
||||
attachAnAudioMessageLabel.setBackgroundTint(it.theme); attachAnAudioMessageLabel.setTint(it.textPrimary)
|
||||
gallery.setBackgroundTint(it.theme); gallery.setTint(it.textPrimary)
|
||||
galleryLabel.setBackgroundTint(it.theme); galleryLabel.setTint(it.textPrimary)
|
||||
camera.setBackgroundTint(it.theme); camera.setTint(it.textPrimary)
|
||||
cameraLabel.setBackgroundTint(it.theme); cameraLabel.setTint(it.textPrimary)
|
||||
binding.attach.setBackgroundTint(it.theme); binding.attach.setTint(it.textPrimary)
|
||||
binding.contact.setBackgroundTint(it.theme); binding.contact.setTint(it.textPrimary)
|
||||
binding.contactLabel.setBackgroundTint(it.theme); binding.contactLabel.setTint(it.textPrimary)
|
||||
binding.schedule.setBackgroundTint(it.theme); binding.schedule.setTint(it.textPrimary)
|
||||
binding.scheduleLabel.setBackgroundTint(it.theme); binding.scheduleLabel.setTint(it.textPrimary)
|
||||
binding.attachAFileIcon.setBackgroundTint(it.theme); binding.attachAFileIcon.setTint(it.textPrimary)
|
||||
binding.attachAFileLabel.setBackgroundTint(it.theme); binding.attachAFileLabel.setTint(it.textPrimary)
|
||||
binding.attachAnAudioMessageIcon.setBackgroundTint(it.theme); binding.attachAnAudioMessageIcon.setTint(it.textPrimary)
|
||||
binding.attachAnAudioMessageLabel.setBackgroundTint(it.theme); binding.attachAnAudioMessageLabel.setTint(it.textPrimary)
|
||||
binding.gallery.setBackgroundTint(it.theme); binding.gallery.setTint(it.textPrimary)
|
||||
binding.galleryLabel.setBackgroundTint(it.theme); binding.galleryLabel.setTint(it.textPrimary)
|
||||
binding.camera.setBackgroundTint(it.theme); binding.camera.setTint(it.textPrimary)
|
||||
binding.cameraLabel.setBackgroundTint(it.theme); binding.cameraLabel.setTint(it.textPrimary)
|
||||
|
||||
// speech to text floating button
|
||||
speechToTextIconBorder.setBackgroundTint(it.theme)
|
||||
speechToTextIcon.setBackgroundTint(it.textPrimary)
|
||||
speechToTextIcon.setTint(it.theme)
|
||||
binding.speechToTextIconBorder.setBackgroundTint(it.theme)
|
||||
binding.speechToTextIcon.setBackgroundTint(it.textPrimary)
|
||||
binding.speechToTextIcon.setTint(it.theme)
|
||||
|
||||
// audio message recording
|
||||
audioMsgRecord.setColor(it.theme)
|
||||
audioMsgPlayerPlayPause.setTint(it.theme)
|
||||
audioMsgPlayerSeekBar.apply {
|
||||
binding.audioMsgRecord.setColor(it.theme)
|
||||
binding.audioMsgPlayerPlayPause.setTint(it.theme)
|
||||
binding.audioMsgPlayerSeekBar.apply {
|
||||
thumbTintList = ColorStateList.valueOf(it.theme)
|
||||
progressBackgroundTintList = ColorStateList.valueOf(it.theme)
|
||||
progressTintList = ColorStateList.valueOf(it.theme)
|
||||
@@ -296,23 +247,23 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
|
||||
// drag drop handlers for speech-to-text icon
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
speechToTextIcon.setOnLongClickListener {
|
||||
it.startDragAndDrop(null, View.DragShadowBuilder(speechToTextFrame), null, 0)
|
||||
speechToTextFrame.isVisible = false
|
||||
binding.speechToTextIcon.setOnLongClickListener {
|
||||
it.startDragAndDrop(null, View.DragShadowBuilder(binding.speechToTextFrame), null, 0)
|
||||
binding.speechToTextFrame.isVisible = false
|
||||
|
||||
contentView.setOnDragListener { _, event ->
|
||||
binding.contentView.setOnDragListener { _, event ->
|
||||
when (event.action) {
|
||||
ACTION_DROP -> {
|
||||
speechToTextFrame.x = (event.x - (speechToTextFrame.width / 2))
|
||||
speechToTextFrame.y = (event.y - (speechToTextFrame.height / 2))
|
||||
binding.speechToTextFrame.x = (event.x - (binding.speechToTextFrame.width / 2))
|
||||
binding.speechToTextFrame.y = (event.y - (binding.speechToTextFrame.height / 2))
|
||||
|
||||
// get offset from root view as a percentage of root view for saving
|
||||
prefs.showSttOffsetX.set((speechToTextFrame.x - contentView.x) / contentView.width)
|
||||
prefs.showSttOffsetY.set((speechToTextFrame.y - contentView.y) / contentView.height)
|
||||
prefs.showSttOffsetX.set((binding.speechToTextFrame.x - binding.contentView.x) / binding.contentView.width)
|
||||
prefs.showSttOffsetY.set((binding.speechToTextFrame.y - binding.contentView.y) / binding.contentView.height)
|
||||
}
|
||||
|
||||
ACTION_DRAG_ENDED, ACTION_DRAG_EXITED -> {
|
||||
speechToTextFrame.isVisible = true
|
||||
binding.speechToTextFrame.isVisible = true
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -322,8 +273,8 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}
|
||||
|
||||
// start/stop audio message recording
|
||||
audioMsgRecord.setOnClickListener {
|
||||
recordAudioRecord.onNext(audioMsgRecord.getState())
|
||||
binding.audioMsgRecord.setOnClickListener {
|
||||
recordAudioRecord.onNext(binding.audioMsgRecord.getState())
|
||||
}
|
||||
|
||||
recordAudioChronometer
|
||||
@@ -332,17 +283,17 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
if (it) {
|
||||
audioMsgDuration.base = SystemClock.elapsedRealtime()
|
||||
audioMsgDuration.start()
|
||||
binding.audioMsgDuration.base = SystemClock.elapsedRealtime()
|
||||
binding.audioMsgDuration.start()
|
||||
} else {
|
||||
audioMsgDuration.stop()
|
||||
binding.audioMsgDuration.stop()
|
||||
}
|
||||
}
|
||||
|
||||
// audio record playback play/pause button
|
||||
audioMsgPlayerPlayPause.setOnClickListener {
|
||||
binding.audioMsgPlayerPlayPause.setOnClickListener {
|
||||
recordAudioPlayerPlayPause.onNext(
|
||||
audioMsgPlayerPlayPause.tag as QkMediaPlayer.PlayingState
|
||||
binding.audioMsgPlayerPlayPause.tag as QkMediaPlayer.PlayingState
|
||||
)
|
||||
}
|
||||
|
||||
@@ -351,10 +302,10 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
.distinctUntilChanged()
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
audioMsgRecord.isVisible = it
|
||||
audioMsgDuration.isVisible =
|
||||
binding.audioMsgRecord.isVisible = it
|
||||
binding.audioMsgDuration.isVisible =
|
||||
it // chronometer follows record button visibility
|
||||
audioMsgBluetooth.isVisible = !it
|
||||
binding.audioMsgBluetooth.isVisible = !it
|
||||
}
|
||||
|
||||
recordAudioPlayerVisible
|
||||
@@ -362,7 +313,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
.distinctUntilChanged()
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
audioMsgPlayerBackground.isVisible = it
|
||||
binding.audioMsgPlayerBackground.isVisible = it
|
||||
recordAudioPlayerConfigUI.onNext(QkMediaPlayer.PlayingState.Stopped)
|
||||
}
|
||||
|
||||
@@ -373,35 +324,35 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
.subscribe {
|
||||
when (it) {
|
||||
QkMediaPlayer.PlayingState.Playing -> {
|
||||
audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Playing
|
||||
binding.audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Playing
|
||||
QkMediaPlayer.start()
|
||||
audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_pause)
|
||||
binding.audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_pause)
|
||||
seekBarUpdater = getSeekBarUpdater().subscribe {
|
||||
audioMsgPlayerSeekBar.progress = QkMediaPlayer.currentPosition
|
||||
audioMsgPlayerSeekBar.max = QkMediaPlayer.duration
|
||||
binding.audioMsgPlayerSeekBar.progress = QkMediaPlayer.currentPosition
|
||||
binding.audioMsgPlayerSeekBar.max = QkMediaPlayer.duration
|
||||
}
|
||||
audioMsgPlayerSeekBar.isEnabled = true
|
||||
binding.audioMsgPlayerSeekBar.isEnabled = true
|
||||
}
|
||||
|
||||
QkMediaPlayer.PlayingState.Paused -> {
|
||||
audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Paused
|
||||
binding.audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Paused
|
||||
QkMediaPlayer.pause()
|
||||
audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_play)
|
||||
binding.audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_play)
|
||||
seekBarUpdater?.dispose()
|
||||
}
|
||||
|
||||
else -> {
|
||||
audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Stopped
|
||||
binding.audioMsgPlayerPlayPause.tag = QkMediaPlayer.PlayingState.Stopped
|
||||
QkMediaPlayer.reset()
|
||||
audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_play)
|
||||
binding.audioMsgPlayerPlayPause.setImageResource(R.drawable.exo_icon_play)
|
||||
seekBarUpdater?.dispose()
|
||||
audioMsgPlayerSeekBar.progress = 0
|
||||
audioMsgPlayerSeekBar.isEnabled = false
|
||||
binding.audioMsgPlayerSeekBar.progress = 0
|
||||
binding.audioMsgPlayerSeekBar.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
// audio msg player seek bar handler
|
||||
audioMsgPlayerSeekBar.setOnSeekBarChangeListener(
|
||||
binding.audioMsgPlayerSeekBar.setOnSeekBarChangeListener(
|
||||
object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(p0: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
// if seek was initiated by the user and this part is currently playing
|
||||
@@ -460,62 +411,62 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
else -> state.conversationtitle
|
||||
}
|
||||
|
||||
toolbarSubtitle.setVisible(state.query.isNotEmpty())
|
||||
toolbarSubtitle.text = getString(R.string.compose_subtitle_results, state.searchSelectionPosition,
|
||||
binding.toolbarSubtitle.setVisible(state.query.isNotEmpty())
|
||||
binding.toolbarSubtitle.text = getString(R.string.compose_subtitle_results, state.searchSelectionPosition,
|
||||
state.searchResults)
|
||||
|
||||
toolbarTitle.setVisible(!state.editingMode)
|
||||
chips.setVisible(state.editingMode)
|
||||
composeBar.setVisible(!state.loading)
|
||||
binding.toolbarTitle.setVisible(!state.editingMode)
|
||||
binding.chips.setVisible(state.editingMode)
|
||||
binding.composeBar.setVisible(!state.loading)
|
||||
|
||||
// Don't set the adapters unless needed
|
||||
if (state.editingMode && chips.adapter == null) chips.adapter = chipsAdapter
|
||||
if (state.editingMode && binding.chips.adapter == null) binding.chips.adapter = chipsAdapter
|
||||
|
||||
toolbar.menu.findItem(R.id.viewScheduledMessages)?.isVisible = !state.editingMode && state.selectedMessages == 0
|
||||
binding.toolbar.menu.findItem(R.id.viewScheduledMessages)?.isVisible = !state.editingMode && state.selectedMessages == 0
|
||||
&& state.query.isEmpty() && state.hasScheduledMessages
|
||||
toolbar.menu.findItem(R.id.select_all)?.isVisible = !state.editingMode && (messageAdapter.itemCount > 1) && state.selectedMessages != 0
|
||||
toolbar.menu.findItem(R.id.add)?.isVisible = state.editingMode
|
||||
toolbar.menu.findItem(R.id.call)?.isVisible = !state.editingMode && state.selectedMessages == 0
|
||||
binding.toolbar.menu.findItem(R.id.select_all)?.isVisible = !state.editingMode && (messageAdapter.itemCount > 1) && state.selectedMessages != 0
|
||||
binding.toolbar.menu.findItem(R.id.add)?.isVisible = state.editingMode
|
||||
binding.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
|
||||
binding.toolbar.menu.findItem(R.id.info)?.isVisible = !state.editingMode && state.selectedMessages == 0
|
||||
&& state.query.isEmpty()
|
||||
toolbar.menu.findItem(R.id.copy)?.isVisible =
|
||||
binding.toolbar.menu.findItem(R.id.copy)?.isVisible =
|
||||
!state.editingMode && state.selectedMessages > 0 && state.selectedMessagesHaveText
|
||||
toolbar.menu.findItem(R.id.share)?.isVisible =
|
||||
binding.toolbar.menu.findItem(R.id.share)?.isVisible =
|
||||
!state.editingMode && state.selectedMessages > 0 && state.selectedMessagesHaveText
|
||||
toolbar.menu.findItem(R.id.details)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
||||
toolbar.menu.findItem(R.id.delete)?.isVisible = !state.editingMode && ((state.selectedMessages > 0) || state.canSend)
|
||||
toolbar.menu.findItem(R.id.forward)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
||||
toolbar.menu.findItem(R.id.show_status)?.isVisible = !state.editingMode && state.selectedMessages > 0
|
||||
toolbar.menu.findItem(R.id.previous)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()
|
||||
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()
|
||||
binding.toolbar.menu.findItem(R.id.details)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
||||
binding.toolbar.menu.findItem(R.id.delete)?.isVisible = !state.editingMode && ((state.selectedMessages > 0) || state.canSend)
|
||||
binding.toolbar.menu.findItem(R.id.forward)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
||||
binding.toolbar.menu.findItem(R.id.show_status)?.isVisible = !state.editingMode && state.selectedMessages > 0
|
||||
binding.toolbar.menu.findItem(R.id.previous)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()
|
||||
binding.toolbar.menu.findItem(R.id.next)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()
|
||||
binding.toolbar.menu.findItem(R.id.clear)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()
|
||||
|
||||
chipsAdapter.data = state.selectedChips
|
||||
|
||||
loading.setVisible(state.loading)
|
||||
binding.loading.setVisible(state.loading)
|
||||
|
||||
sendAsGroup.setVisible(state.recipientCount > 1)
|
||||
sendAsGroupSwitch.isChecked = state.sendAsGroup
|
||||
sendAsGroupSummary.setText(
|
||||
if (sendAsGroupSwitch.isChecked) R.string.compose_send_group_summary_on
|
||||
binding.sendAsGroup.setVisible(state.recipientCount > 1)
|
||||
binding.sendAsGroupSwitch.isChecked = state.sendAsGroup
|
||||
binding.sendAsGroupSummary.setText(
|
||||
if (binding.sendAsGroupSwitch.isChecked) R.string.compose_send_group_summary_on
|
||||
else R.string.compose_send_group_summary_off
|
||||
)
|
||||
|
||||
messageList.setVisible(!state.editingMode || state.sendAsGroup || state.selectedChips.size == 1)
|
||||
binding.messageList.setVisible(!state.editingMode || state.sendAsGroup || state.selectedChips.size == 1)
|
||||
messageAdapter.data = state.messages
|
||||
messageAdapter.highlight = state.searchSelectionId
|
||||
|
||||
scheduledGroup.isVisible = state.scheduled != 0L
|
||||
scheduledTime.text = dateFormatter.getScheduledTimestamp(state.scheduled)
|
||||
binding.scheduledGroup.isVisible = state.scheduled != 0L
|
||||
binding.scheduledTime.text = dateFormatter.getScheduledTimestamp(state.scheduled)
|
||||
|
||||
messageAttachments.setVisible(state.attachments.isNotEmpty())
|
||||
binding.messageAttachments.setVisible(state.attachments.isNotEmpty())
|
||||
composeAttachmentAdapter.data = state.attachments
|
||||
|
||||
attach.animate().rotation(if (state.attaching) 135f else 0f).start()
|
||||
attaching.isVisible = state.attaching
|
||||
binding.attach.animate().rotation(if (state.attaching) 135f else 0f).start()
|
||||
binding.attaching.isVisible = state.attaching
|
||||
|
||||
shadeBackground.apply {
|
||||
binding.shadeBackground.apply {
|
||||
when {
|
||||
state.attaching -> {
|
||||
visibility = View.VISIBLE
|
||||
@@ -532,31 +483,31 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}
|
||||
|
||||
// show or hide audio message recording panel and shade background
|
||||
audioMsgBackground.isVisible = state.audioMsgRecording
|
||||
binding.audioMsgBackground.isVisible = state.audioMsgRecording
|
||||
|
||||
counter.text = state.remaining
|
||||
counter.setVisible(counter.text.isNotBlank())
|
||||
binding.counter.text = state.remaining
|
||||
binding.counter.setVisible(binding.counter.text.isNotBlank())
|
||||
|
||||
sim.setVisible(state.subscription != null)
|
||||
sim.contentDescription = getString(R.string.compose_sim_cd, state.subscription?.displayName)
|
||||
simIndex.text = state.subscription?.simSlotIndex?.plus(1)?.toString()
|
||||
binding.sim.setVisible(state.subscription != null)
|
||||
binding.sim.contentDescription = getString(R.string.compose_sim_cd, state.subscription?.displayName)
|
||||
binding.simIndex.text = state.subscription?.simSlotIndex?.plus(1)?.toString()
|
||||
|
||||
// show either send, audio msg record, or sendScheduled button
|
||||
send.visibility = if (state.canSend && !state.loading && state.scheduled == 0L) View.VISIBLE else View.INVISIBLE
|
||||
recordAudioMsg.visibility = if (state.canSend && !state.loading) View.INVISIBLE else View.VISIBLE
|
||||
scheduledSend.visibility = if (state.canSend && (state.scheduled != 0L) && !state.loading) View.VISIBLE else View.INVISIBLE
|
||||
binding.send.visibility = if (state.canSend && !state.loading && state.scheduled == 0L) View.VISIBLE else View.INVISIBLE
|
||||
binding.recordAudioMsg.visibility = if (state.canSend && !state.loading) View.INVISIBLE else View.VISIBLE
|
||||
binding.scheduledSend.visibility = if (state.canSend && (state.scheduled != 0L) && !state.loading) View.VISIBLE else View.INVISIBLE
|
||||
|
||||
// if not in editing mode, and there are no non-me participants that can be sent to,
|
||||
// hide controls that allow constructing a reply and inform user no valid recipients
|
||||
if (!state.editingMode && (state.validRecipientNumbers == 0)) {
|
||||
composeBar.visibility = View.GONE
|
||||
sim.visibility = View.GONE
|
||||
recordAudioMsg.visibility = View.GONE
|
||||
noValidRecipients.visibility = View.VISIBLE
|
||||
binding.composeBar.visibility = View.GONE
|
||||
binding.sim.visibility = View.GONE
|
||||
binding.recordAudioMsg.visibility = View.GONE
|
||||
binding.noValidRecipients.visibility = View.VISIBLE
|
||||
|
||||
// change constraint of messageList to constrain bottom to top of noValidRecipients
|
||||
ConstraintSet().apply {
|
||||
clone(contentView)
|
||||
clone(binding.contentView)
|
||||
connect(
|
||||
R.id.messageList,
|
||||
ConstraintSet.BOTTOM,
|
||||
@@ -564,7 +515,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
ConstraintSet.TOP,
|
||||
0
|
||||
)
|
||||
applyTo(contentView)
|
||||
applyTo(binding.contentView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,15 +525,15 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
|
||||
// if stt is available and preference is set to show stt button
|
||||
if (isSpeechRecognitionAvailable() && prefs.showStt.get()) {
|
||||
speechToTextFrame.isVisible = true
|
||||
binding.speechToTextFrame.isVisible = true
|
||||
|
||||
val xPercent = prefs.showSttOffsetX.get()
|
||||
val yPercent = prefs.showSttOffsetY.get()
|
||||
|
||||
// if the stt icon has a custom position, move it
|
||||
if ((xPercent != Float.MAX_VALUE) && (yPercent != Float.MAX_VALUE)) {
|
||||
speechToTextFrame.x = (contentView.x + (xPercent * contentView.width))
|
||||
speechToTextFrame.y = (contentView.y + (yPercent * contentView.height))
|
||||
binding.speechToTextFrame.x = (binding.contentView.x + (xPercent * binding.contentView.width))
|
||||
binding.speechToTextFrame.y = (binding.contentView.y + (yPercent * binding.contentView.height))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,7 +606,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)).show()
|
||||
|
||||
// On some devices, the keyboard can cover the date picker
|
||||
message.hideKeyboard()
|
||||
binding.message.hideKeyboard()
|
||||
}
|
||||
|
||||
override fun requestContact() {
|
||||
@@ -666,7 +617,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}
|
||||
|
||||
override fun showContacts(sharing: Boolean, chips: List<Recipient>) {
|
||||
message.hideKeyboard()
|
||||
binding.message.hideKeyboard()
|
||||
val serialized = HashMap(chips.associate { chip -> chip.address to chip.contact?.lookupKey })
|
||||
val intent = Intent(this, ContactsActivity::class.java)
|
||||
.putExtra(ContactsActivity.SHARING_KEY, sharing)
|
||||
@@ -688,12 +639,12 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}
|
||||
|
||||
override fun themeChanged() {
|
||||
messageList.scrapViews()
|
||||
binding.messageList.scrapViews()
|
||||
}
|
||||
|
||||
override fun showKeyboard() {
|
||||
message.postDelayed({
|
||||
message.showKeyboard()
|
||||
binding.message.postDelayed({
|
||||
binding.message.showKeyboard()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
@@ -718,19 +669,19 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
}
|
||||
|
||||
override fun setDraft(draft: String) {
|
||||
message.setText(draft)
|
||||
message.setSelection(draft.length)
|
||||
binding.message.setText(draft)
|
||||
binding.message.setSelection(draft.length)
|
||||
}
|
||||
|
||||
override fun scrollToMessage(id: Long) {
|
||||
messageAdapter.data?.second
|
||||
?.indexOfLast { message -> message.id == id }
|
||||
?.takeIf { position -> position != -1 }
|
||||
?.let(messageList::scrollToPosition)
|
||||
?.let(binding.messageList::scrollToPosition)
|
||||
}
|
||||
|
||||
override fun showQksmsPlusSnackbar(message: Int) {
|
||||
Snackbar.make(contentView, message, Snackbar.LENGTH_LONG).run {
|
||||
Snackbar.make(binding.contentView, message, Snackbar.LENGTH_LONG).run {
|
||||
setAction(R.string.button_more) { viewQksmsPlusIntent.onNext(Unit) }
|
||||
setActionTextColor(colors.theme().theme)
|
||||
show()
|
||||
@@ -825,14 +776,10 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
// check returned results are good
|
||||
val match = data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||
if ((match !== null) && (match.size > 0) && (!match[0].isNullOrEmpty())) {
|
||||
// get the edit text view
|
||||
val messageEditBox = findViewById<QkEditText>(R.id.message)
|
||||
if (messageEditBox !== null) {
|
||||
// populate message box with data returned by STT, set cursor to end, and focus
|
||||
messageEditBox.append(match[0])
|
||||
messageEditBox.setSelection(messageEditBox.text?.length ?: 0)
|
||||
messageEditBox.requestFocus()
|
||||
}
|
||||
// populate message box with data returned by STT, set cursor to end, and focus
|
||||
binding.message.append(match[0])
|
||||
binding.message.setSelection(binding.message.text?.length ?: 0)
|
||||
binding.message.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,6 +800,6 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
|
||||
override fun onBackPressed() = backPressedIntent.onNext(Unit)
|
||||
|
||||
override fun focusMessage() {
|
||||
message.requestFocus()
|
||||
binding.message.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -27,6 +27,8 @@ import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.extensions.getDisplayName
|
||||
import dev.octoshrimpy.quik.databinding.AttachmentContactListItemBinding
|
||||
import dev.octoshrimpy.quik.databinding.AttachmentFileListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.getName
|
||||
import dev.octoshrimpy.quik.feature.extensions.LoadBestIconIntoImageView
|
||||
import dev.octoshrimpy.quik.feature.extensions.loadBestIconIntoImageView
|
||||
@@ -34,9 +36,6 @@ import dev.octoshrimpy.quik.model.Attachment
|
||||
import ezvcard.Ezvcard
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.attachment_contact_list_item.*
|
||||
import kotlinx.android.synthetic.main.scheduled_message_image_list_item.fileName
|
||||
import kotlinx.android.synthetic.main.scheduled_message_image_list_item.thumbnail
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -54,12 +53,11 @@ class ComposeAttachmentAdapter @Inject constructor(
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
val view = inflater.inflate(
|
||||
if (viewType == VIEW_TYPE_CONTACT) R.layout.attachment_contact_list_item
|
||||
else R.layout.attachment_file_list_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
val view = if (viewType == VIEW_TYPE_CONTACT) {
|
||||
AttachmentContactListItemBinding.inflate(inflater, parent, false).root
|
||||
} else {
|
||||
AttachmentFileListItemBinding.inflate(inflater, parent, false).root
|
||||
}
|
||||
|
||||
return QkViewHolder(view).apply {
|
||||
view.setOnClickListener {
|
||||
@@ -73,36 +71,38 @@ class ComposeAttachmentAdapter @Inject constructor(
|
||||
val attachment = getItem(position)
|
||||
|
||||
if (attachment.isVCard(context)) {
|
||||
val binding = AttachmentContactListItemBinding.bind(holder.itemView)
|
||||
try {
|
||||
val displayName = Ezvcard.parse(
|
||||
String(attachment.getResourceBytes(context))
|
||||
).first().getDisplayName() ?: ""
|
||||
holder.name.text = displayName
|
||||
holder.name.isVisible = displayName.isNotEmpty()
|
||||
binding.name.text = displayName
|
||||
binding.name.isVisible = displayName.isNotEmpty()
|
||||
} catch (e: Exception) {
|
||||
// npe from Ezvcard first() call above can be thrown if resource bytes cannot
|
||||
// be retrieved from contact resource provider
|
||||
holder.vCardAvatar.setImageResource(android.R.drawable.ic_delete)
|
||||
holder.name.text = context.getString(R.string.attachment_missing)
|
||||
holder.name.isVisible = true
|
||||
binding.vCardAvatar.setImageResource(android.R.drawable.ic_delete)
|
||||
binding.name.text = context.getString(R.string.attachment_missing)
|
||||
binding.name.isVisible = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val binding = AttachmentFileListItemBinding.bind(holder.itemView)
|
||||
// set best image and text to use for icon
|
||||
when (attachment.uri.loadBestIconIntoImageView(context, holder.thumbnail)) {
|
||||
when (attachment.uri.loadBestIconIntoImageView(context, binding.thumbnail)) {
|
||||
LoadBestIconIntoImageView.Missing -> {
|
||||
holder.fileName.text = context.getString(R.string.attachment_missing)
|
||||
holder.fileName.visibility = View.VISIBLE
|
||||
binding.fileName.text = context.getString(R.string.attachment_missing)
|
||||
binding.fileName.visibility = View.VISIBLE
|
||||
}
|
||||
LoadBestIconIntoImageView.ActivityIcon,
|
||||
LoadBestIconIntoImageView.DefaultAudioIcon,
|
||||
LoadBestIconIntoImageView.GenericIcon -> {
|
||||
// generic style icon used, also show name
|
||||
holder.fileName.text = attachment.uri.getName(context)
|
||||
holder.fileName.visibility = View.VISIBLE
|
||||
binding.fileName.text = attachment.uri.getName(context)
|
||||
binding.fileName.visibility = View.VISIBLE
|
||||
}
|
||||
else -> holder.fileName.visibility = View.GONE
|
||||
else -> binding.fileName.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import com.moez.QKSMS.common.QkMediaPlayer
|
||||
@@ -65,24 +66,14 @@ import dev.octoshrimpy.quik.feature.extensions.isEmojiOnly
|
||||
import dev.octoshrimpy.quik.model.Conversation
|
||||
import dev.octoshrimpy.quik.model.Message
|
||||
import dev.octoshrimpy.quik.model.Recipient
|
||||
import dev.octoshrimpy.quik.databinding.MessageListItemInBinding
|
||||
import dev.octoshrimpy.quik.databinding.MessageListItemOutBinding
|
||||
import dev.octoshrimpy.quik.util.PhoneNumberUtils
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import io.realm.RealmResults
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.*
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.body
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.reactionText
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.parts
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.reactions
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.sim
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.simIndex
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.status
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.timestamp
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.view.*
|
||||
import kotlinx.android.synthetic.main.message_list_item_out.*
|
||||
import kotlinx.android.synthetic.main.message_list_item_out.view.cancel
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
@@ -149,20 +140,33 @@ class MessagesAdapter @Inject constructor(
|
||||
// Use the parent's context to inflate the layout, otherwise link clicks will crash the app
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
val view = if (viewType == VIEW_TYPE_MESSAGE_OUT) {
|
||||
inflater.inflate(R.layout.message_list_item_out, parent,false).apply {
|
||||
findViewById<ImageView>(R.id.cancelIcon).setTint(theme.theme)
|
||||
findViewById<ProgressBar>(R.id.cancel).setTint(theme.theme)
|
||||
findViewById<ImageView>(R.id.sendNowIcon).setTint(theme.theme)
|
||||
findViewById<ImageView>(R.id.resendIcon).setTint(theme.theme)
|
||||
}
|
||||
} else
|
||||
inflater.inflate(R.layout.message_list_item_in, parent, false)
|
||||
val view: View
|
||||
val body: TextView
|
||||
val parts: View
|
||||
val status: View
|
||||
|
||||
view.body.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
|
||||
if (viewType == VIEW_TYPE_MESSAGE_OUT) {
|
||||
val binding = MessageListItemOutBinding.inflate(inflater, parent, false)
|
||||
view = binding.root
|
||||
body = binding.body
|
||||
parts = binding.parts
|
||||
status = binding.status
|
||||
binding.cancelIcon.setTint(theme.theme)
|
||||
binding.cancel.setTint(theme.theme)
|
||||
binding.sendNowIcon.setTint(theme.theme)
|
||||
binding.resendIcon.setTint(theme.theme)
|
||||
} else {
|
||||
val binding = MessageListItemInBinding.inflate(inflater, parent, false)
|
||||
view = binding.root
|
||||
body = binding.body
|
||||
parts = binding.parts
|
||||
status = binding.status
|
||||
}
|
||||
|
||||
body.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
|
||||
|
||||
// register recycler view with compose activity for context menus
|
||||
partContextMenuRegistrar.onNext(view.parts)
|
||||
partContextMenuRegistrar.onNext(parts)
|
||||
|
||||
return QkViewHolder(view).apply {
|
||||
view.setOnClickListener {
|
||||
@@ -170,7 +174,7 @@ class MessagesAdapter @Inject constructor(
|
||||
when (toggleSelection(it.id, false)) {
|
||||
true -> view.isActivated = isSelected(it.id)
|
||||
false -> {
|
||||
expanded[it.id] = view.status.visibility != View.VISIBLE
|
||||
expanded[it.id] = status.visibility != View.VISIBLE
|
||||
notifyItemChanged(adapterPosition)
|
||||
}
|
||||
}
|
||||
@@ -197,55 +201,108 @@ class MessagesAdapter @Inject constructor(
|
||||
}
|
||||
|
||||
// Update the selected state
|
||||
holder.containerView.isActivated = isSelected(message.id) || highlight == message.id
|
||||
holder.itemView.isActivated = isSelected(message.id) || highlight == message.id
|
||||
|
||||
// bind the cancelFrame (cancel button) and send now button
|
||||
holder.cancelFrame?.let { cancelFrame ->
|
||||
holder.sendNowIcon?.let { sendNowIcon ->
|
||||
val isCancellable = message.isSending() && message.date > System.currentTimeMillis()
|
||||
// Get views based on message type
|
||||
val isOutgoing = message.isMe()
|
||||
val timestamp: TextView
|
||||
val simIndex: TextView
|
||||
val sim: ImageView
|
||||
val body: TextView
|
||||
val parts: androidx.recyclerview.widget.RecyclerView
|
||||
val reactions: View
|
||||
val reactionText: TextView
|
||||
val status: TextView
|
||||
|
||||
if (isCancellable) {
|
||||
cancelFrame.visibility = View.VISIBLE
|
||||
sendNowIcon.visibility = View.VISIBLE
|
||||
if (isOutgoing) {
|
||||
val binding = MessageListItemOutBinding.bind(holder.itemView)
|
||||
timestamp = binding.timestamp
|
||||
simIndex = binding.simIndex
|
||||
sim = binding.sim
|
||||
body = binding.body
|
||||
parts = binding.parts
|
||||
reactions = binding.reactions
|
||||
reactionText = binding.reactionText
|
||||
status = binding.status
|
||||
|
||||
cancelFrame.setOnClickListener { cancelSendingClicks.onNext(message.id) }
|
||||
sendNowIcon.setOnClickListener { sendNowClicks.onNext(message.id) }
|
||||
// bind the cancelFrame (cancel button) and send now button
|
||||
val isCancellable = message.isSending() && message.date > System.currentTimeMillis()
|
||||
|
||||
cancelFrame.cancel.progress = 2
|
||||
if (isCancellable) {
|
||||
binding.cancelFrame.visibility = View.VISIBLE
|
||||
binding.sendNowIcon.visibility = View.VISIBLE
|
||||
|
||||
val delay = when (prefs.sendDelay.get()) {
|
||||
Preferences.SEND_DELAY_SHORT -> 3000
|
||||
Preferences.SEND_DELAY_MEDIUM -> 5000
|
||||
Preferences.SEND_DELAY_LONG -> 10000
|
||||
else -> 0
|
||||
}
|
||||
val progress =
|
||||
(1 - (message.date - System.currentTimeMillis()) / delay.toFloat()) * 100
|
||||
binding.cancelFrame.setOnClickListener { cancelSendingClicks.onNext(message.id) }
|
||||
binding.sendNowIcon.setOnClickListener { sendNowClicks.onNext(message.id) }
|
||||
|
||||
ObjectAnimator.ofInt(cancelFrame.cancel, "progress", progress.toInt(), 100)
|
||||
.setDuration(message.date - System.currentTimeMillis())
|
||||
.start()
|
||||
binding.cancel.progress = 2
|
||||
|
||||
val delay = when (prefs.sendDelay.get()) {
|
||||
Preferences.SEND_DELAY_SHORT -> 3000
|
||||
Preferences.SEND_DELAY_MEDIUM -> 5000
|
||||
Preferences.SEND_DELAY_LONG -> 10000
|
||||
else -> 0
|
||||
}
|
||||
else {
|
||||
cancelFrame.visibility = View.GONE
|
||||
sendNowIcon.visibility = View.GONE
|
||||
val progress =
|
||||
(1 - (message.date - System.currentTimeMillis()) / delay.toFloat()) * 100
|
||||
|
||||
cancelFrame.setOnClickListener(null)
|
||||
sendNowIcon.setOnClickListener(null)
|
||||
ObjectAnimator.ofInt(binding.cancel, "progress", progress.toInt(), 100)
|
||||
.setDuration(message.date - System.currentTimeMillis())
|
||||
.start()
|
||||
} else {
|
||||
binding.cancelFrame.visibility = View.GONE
|
||||
binding.sendNowIcon.visibility = View.GONE
|
||||
|
||||
binding.cancelFrame.setOnClickListener(null)
|
||||
binding.sendNowIcon.setOnClickListener(null)
|
||||
}
|
||||
|
||||
// bind the resend icon view
|
||||
if (message.isFailedMessage()) {
|
||||
binding.resendIcon.visibility = View.VISIBLE
|
||||
binding.resendIcon.clicks().subscribe {
|
||||
resendClicks.onNext(message.id)
|
||||
binding.resendIcon.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
binding.resendIcon.visibility = View.GONE
|
||||
}
|
||||
|
||||
body.apply {
|
||||
highlightColor = theme.theme.withAlpha(0x5d)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
textSelectHandle?.setTint(theme.theme.withAlpha(0xad))
|
||||
textSelectHandleLeft?.setTint(theme.theme.withAlpha(0xad))
|
||||
textSelectHandleRight?.setTint(theme.theme.withAlpha(0xad))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val binding = MessageListItemInBinding.bind(holder.itemView)
|
||||
timestamp = binding.timestamp
|
||||
simIndex = binding.simIndex
|
||||
sim = binding.sim
|
||||
body = binding.body
|
||||
parts = binding.parts
|
||||
reactions = binding.reactions
|
||||
reactionText = binding.reactionText
|
||||
status = binding.status
|
||||
|
||||
// bind the resend icon view
|
||||
holder.resendIcon?.let { resendIcon ->
|
||||
if (message.isFailedMessage()) {
|
||||
resendIcon.visibility = View.VISIBLE
|
||||
resendIcon.clicks().subscribe {
|
||||
resendClicks.onNext(message.id)
|
||||
resendIcon.visibility = View.GONE
|
||||
// Bind the avatar and bubble colour
|
||||
binding.avatar.apply {
|
||||
setRecipient(contactCache[message.address])
|
||||
setVisible(!canGroup(message, next), View.INVISIBLE)
|
||||
}
|
||||
|
||||
body.apply {
|
||||
setTextColor(theme.textPrimary)
|
||||
setBackgroundTint(theme.theme)
|
||||
highlightColor = R.attr.bubbleColor.withAlpha(0x5d)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
textSelectHandle?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
textSelectHandleLeft?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
textSelectHandleRight?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
}
|
||||
} else
|
||||
resendIcon.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
val subject = message.getCleansedSubject()
|
||||
@@ -271,12 +328,12 @@ class MessagesAdapter @Inject constructor(
|
||||
}
|
||||
|
||||
// Bind the message status
|
||||
bindStatus(holder, isMsgTextTruncated, message, next)
|
||||
bindStatus(status, isMsgTextTruncated, message, next)
|
||||
|
||||
// Bind the timestamp
|
||||
val subscription = subs.find { it.subscriptionId == message.subId }
|
||||
|
||||
holder.timestamp.apply {
|
||||
timestamp.apply {
|
||||
text = dateFormatter.getMessageTimestamp(message.date)
|
||||
setVisible(
|
||||
((message.date - (previous?.date ?: 0))
|
||||
@@ -286,49 +343,22 @@ class MessagesAdapter @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
holder.simIndex.text = subscription?.simSlotIndex?.plus(1)?.toString()
|
||||
simIndex.text = subscription?.simSlotIndex?.plus(1)?.toString()
|
||||
|
||||
((message.subId != previous?.subId) && (subscription != null) && (subs.size > 1)).also {
|
||||
holder.sim.setVisible(it)
|
||||
holder.simIndex.setVisible(it)
|
||||
sim.setVisible(it)
|
||||
simIndex.setVisible(it)
|
||||
}
|
||||
|
||||
// Bind the grouping
|
||||
holder.containerView.setPadding(
|
||||
holder.itemView.setPadding(
|
||||
bottom = if (canGroup(message, next)) 0 else 16.dpToPx(context)
|
||||
)
|
||||
|
||||
// Bind the avatar and bubble colour
|
||||
if (!message.isMe()) {
|
||||
holder.avatar.apply {
|
||||
setRecipient(contactCache[message.address])
|
||||
setVisible(!canGroup(message, next), View.INVISIBLE)
|
||||
}
|
||||
|
||||
holder.body.apply {
|
||||
setTextColor(theme.textPrimary)
|
||||
setBackgroundTint(theme.theme)
|
||||
highlightColor = R.attr.bubbleColor.withAlpha(0x5d)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
textSelectHandle?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
textSelectHandleLeft?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
textSelectHandleRight?.setTint(R.attr.bubbleColor.withAlpha(0x7d))
|
||||
}
|
||||
}
|
||||
} else
|
||||
holder.body.apply {
|
||||
highlightColor = theme.theme.withAlpha(0x5d)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
textSelectHandle?.setTint(theme.theme.withAlpha(0xad))
|
||||
textSelectHandleLeft?.setTint(theme.theme.withAlpha(0xad))
|
||||
textSelectHandleRight?.setTint(theme.theme.withAlpha(0xad))
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the body text
|
||||
val emojiOnly = displayText.isEmojiOnly()
|
||||
textViewStyler.setTextSize(
|
||||
holder.body,
|
||||
body,
|
||||
when (emojiOnly) {
|
||||
true -> TextViewStyler.SIZE_EMOJI
|
||||
false -> TextViewStyler.SIZE_PRIMARY
|
||||
@@ -338,10 +368,10 @@ class MessagesAdapter @Inject constructor(
|
||||
val spanString = SpannableStringBuilder(displayText)
|
||||
|
||||
when (prefs.messageLinkHandling.get()) {
|
||||
Preferences.MESSAGE_LINK_HANDLING_BLOCK -> holder.body.autoLinkMask = 0
|
||||
Preferences.MESSAGE_LINK_HANDLING_BLOCK -> body.autoLinkMask = 0
|
||||
Preferences.MESSAGE_LINK_HANDLING_ASK -> {
|
||||
// manually handle link clicks if user has set to ask before opening links
|
||||
holder.body.apply {
|
||||
body.apply {
|
||||
isClickable = false
|
||||
linksClickable = false
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
@@ -368,10 +398,10 @@ class MessagesAdapter @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> holder.body.movementMethod = LinkMovementMethod.getInstance()
|
||||
else -> body.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
holder.body.apply {
|
||||
body.apply {
|
||||
text = spanString
|
||||
setVisible(message.isSms() || spanString.isNotBlank())
|
||||
|
||||
@@ -387,49 +417,48 @@ class MessagesAdapter @Inject constructor(
|
||||
}
|
||||
|
||||
// Bind the parts
|
||||
holder.parts.adapter = partsAdapterProvider.get().apply {
|
||||
parts.adapter = partsAdapterProvider.get().apply {
|
||||
this.theme = theme
|
||||
setData(message, previous, next, holder, audioState)
|
||||
contextMenuValue = message.id
|
||||
clicks.subscribe(partClicks) // part clicks gets passed back to compose view model
|
||||
}
|
||||
|
||||
showEmojiReactions(holder, message)
|
||||
showEmojiReactions(reactions, reactionText, message)
|
||||
}
|
||||
|
||||
private fun showEmojiReactions(holder: QkViewHolder, message: Message) {
|
||||
holder.reactions?.let { reactionsContainer ->
|
||||
val reactions = message.emojiReactions
|
||||
val hasReactions = reactions.isNotEmpty()
|
||||
private fun showEmojiReactions(reactionsContainer: View, reactionTextView: TextView, message: Message) {
|
||||
val reactions = message.emojiReactions
|
||||
val hasReactions = reactions.isNotEmpty()
|
||||
|
||||
if (hasReactions) {
|
||||
val uniqueEmojis = reactions.map { it.emoji }.distinct()
|
||||
val totalCount = reactions.size
|
||||
if (hasReactions) {
|
||||
val uniqueEmojis = reactions.map { it.emoji }.distinct()
|
||||
val totalCount = reactions.size
|
||||
|
||||
// Show unique emojis followed by total count
|
||||
val reactionText = if (totalCount == 1) {
|
||||
uniqueEmojis.first()
|
||||
} else {
|
||||
"${uniqueEmojis.joinToString("")}\u00A0$totalCount"
|
||||
}
|
||||
|
||||
holder.reactionText?.text = reactionText
|
||||
holder.reactionText?.setOnClickListener { reactionClicks.onNext(message.id) }
|
||||
reactionsContainer.setVisible(true)
|
||||
// Show unique emojis followed by total count
|
||||
val reactionText = if (totalCount == 1) {
|
||||
uniqueEmojis.first()
|
||||
} else {
|
||||
reactionsContainer.setVisible(false)
|
||||
holder.reactionText?.setOnClickListener(null)
|
||||
"${uniqueEmojis.joinToString("")}\u00A0$totalCount"
|
||||
}
|
||||
|
||||
reactionTextView.text = reactionText
|
||||
reactionTextView.setOnClickListener { reactionClicks.onNext(message.id) }
|
||||
reactionsContainer.setVisible(true)
|
||||
} else {
|
||||
reactionsContainer.setVisible(false)
|
||||
reactionTextView.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun bindStatus(
|
||||
holder: QkViewHolder,
|
||||
statusView: TextView,
|
||||
bodyTextTruncated: Boolean,
|
||||
message: Message,
|
||||
next: Message?
|
||||
) {
|
||||
holder.status.apply {
|
||||
statusView.apply {
|
||||
text = when {
|
||||
message.isSending() -> context.getString(R.string.message_status_sending)
|
||||
message.isDelivered() -> context.getString(
|
||||
|
||||
+66
-66
@@ -24,7 +24,7 @@ import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.base.QkBindingViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.forwardTouches
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
@@ -36,16 +36,15 @@ import dev.octoshrimpy.quik.model.Recipient
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepository
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import dev.octoshrimpy.quik.databinding.ContactListItemBinding
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.contact_list_item.*
|
||||
import kotlinx.android.synthetic.main.contact_list_item.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ComposeItemAdapter @Inject constructor(
|
||||
private val colors: Colors,
|
||||
private val conversationRepo: ConversationRepository
|
||||
) : QkAdapter<ComposeItem, QkViewHolder>() {
|
||||
) : QkAdapter<ComposeItem, QkBindingViewHolder<ContactListItemBinding>>() {
|
||||
|
||||
val clicks: Subject<ComposeItem> = PublishSubject.create()
|
||||
val longClicks: Subject<ComposeItem> = PublishSubject.create()
|
||||
@@ -59,22 +58,22 @@ class ComposeItemAdapter @Inject constructor(
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkBindingViewHolder<ContactListItemBinding> {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val view = layoutInflater.inflate(R.layout.contact_list_item, parent, false)
|
||||
val binding = ContactListItemBinding.inflate(layoutInflater, parent, false)
|
||||
|
||||
view.icon.setTint(colors.theme().theme)
|
||||
binding.icon.setTint(colors.theme().theme)
|
||||
|
||||
view.numbers.setRecycledViewPool(numbersViewPool)
|
||||
view.numbers.adapter = PhoneNumberAdapter()
|
||||
view.numbers.forwardTouches(view)
|
||||
binding.numbers.setRecycledViewPool(numbersViewPool)
|
||||
binding.numbers.adapter = PhoneNumberAdapter()
|
||||
binding.numbers.forwardTouches(binding.root)
|
||||
|
||||
return QkViewHolder(view).apply {
|
||||
view.setOnClickListener {
|
||||
return QkBindingViewHolder(binding).apply {
|
||||
binding.root.setOnClickListener {
|
||||
val item = getItem(adapterPosition)
|
||||
clicks.onNext(item)
|
||||
}
|
||||
view.setOnLongClickListener {
|
||||
binding.root.setOnLongClickListener {
|
||||
val item = getItem(adapterPosition)
|
||||
longClicks.onNext(item)
|
||||
true
|
||||
@@ -82,103 +81,104 @@ class ComposeItemAdapter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: QkBindingViewHolder<ContactListItemBinding>, position: Int) {
|
||||
val binding = holder.binding
|
||||
val prevItem = if (position > 0) getItem(position - 1) else null
|
||||
when (val item = getItem(position)) {
|
||||
is ComposeItem.New -> bindNew(holder, item.value)
|
||||
is ComposeItem.Recent -> bindRecent(holder, item.value, prevItem)
|
||||
is ComposeItem.Starred -> bindStarred(holder, item.value, prevItem)
|
||||
is ComposeItem.Person -> bindPerson(holder, item.value, prevItem)
|
||||
is ComposeItem.Group -> bindGroup(holder, item.value, prevItem)
|
||||
is ComposeItem.New -> bindNew(binding, item.value)
|
||||
is ComposeItem.Recent -> bindRecent(binding, item.value, prevItem)
|
||||
is ComposeItem.Starred -> bindStarred(binding, item.value, prevItem)
|
||||
is ComposeItem.Person -> bindPerson(binding, item.value, prevItem)
|
||||
is ComposeItem.Group -> bindGroup(binding, item.value, prevItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindNew(holder: QkViewHolder, contact: Contact) {
|
||||
holder.index.isVisible = false
|
||||
private fun bindNew(binding: ContactListItemBinding, contact: Contact) {
|
||||
binding.index.isVisible = false
|
||||
|
||||
holder.icon.isVisible = false
|
||||
binding.icon.isVisible = false
|
||||
|
||||
holder.avatar.recipients = listOf(createRecipient(contact))
|
||||
binding.avatar.recipients = listOf(createRecipient(contact))
|
||||
|
||||
holder.title.text = contact.numbers.joinToString { it.address }
|
||||
binding.title.text = contact.numbers.joinToString { it.address }
|
||||
|
||||
holder.subtitle.isVisible = false
|
||||
binding.subtitle.isVisible = false
|
||||
|
||||
holder.numbers.isVisible = false
|
||||
binding.numbers.isVisible = false
|
||||
}
|
||||
|
||||
private fun bindRecent(holder: QkViewHolder, conversation: Conversation, prev: ComposeItem?) {
|
||||
holder.index.isVisible = false
|
||||
private fun bindRecent(binding: ContactListItemBinding, conversation: Conversation, prev: ComposeItem?) {
|
||||
binding.index.isVisible = false
|
||||
|
||||
holder.icon.isVisible = prev !is ComposeItem.Recent
|
||||
holder.icon.setImageResource(R.drawable.ic_history_black_24dp)
|
||||
binding.icon.isVisible = prev !is ComposeItem.Recent
|
||||
binding.icon.setImageResource(R.drawable.ic_history_black_24dp)
|
||||
|
||||
holder.avatar.recipients = conversation.recipients
|
||||
binding.avatar.recipients = conversation.recipients
|
||||
|
||||
holder.title.text = conversation.getTitle()
|
||||
binding.title.text = conversation.getTitle()
|
||||
|
||||
holder.subtitle.isVisible = conversation.recipients.size > 1 && conversation.name.isBlank()
|
||||
holder.subtitle.text = conversation.recipients.joinToString(", ") { recipient ->
|
||||
binding.subtitle.isVisible = conversation.recipients.size > 1 && conversation.name.isBlank()
|
||||
binding.subtitle.text = conversation.recipients.joinToString(", ") { recipient ->
|
||||
recipient.contact?.name ?: recipient.address
|
||||
}
|
||||
holder.subtitle.collapseEnabled = conversation.recipients.size > 1
|
||||
binding.subtitle.collapseEnabled = conversation.recipients.size > 1
|
||||
|
||||
holder.numbers.isVisible = conversation.recipients.size == 1
|
||||
(holder.numbers.adapter as PhoneNumberAdapter).data = conversation.recipients
|
||||
binding.numbers.isVisible = conversation.recipients.size == 1
|
||||
(binding.numbers.adapter as PhoneNumberAdapter).data = conversation.recipients
|
||||
.mapNotNull { recipient -> recipient.contact }
|
||||
.flatMap { contact -> contact.numbers }
|
||||
}
|
||||
|
||||
private fun bindStarred(holder: QkViewHolder, contact: Contact, prev: ComposeItem?) {
|
||||
holder.index.isVisible = false
|
||||
private fun bindStarred(binding: ContactListItemBinding, contact: Contact, prev: ComposeItem?) {
|
||||
binding.index.isVisible = false
|
||||
|
||||
holder.icon.isVisible = prev !is ComposeItem.Starred
|
||||
holder.icon.setImageResource(R.drawable.ic_star_black_24dp)
|
||||
binding.icon.isVisible = prev !is ComposeItem.Starred
|
||||
binding.icon.setImageResource(R.drawable.ic_star_black_24dp)
|
||||
|
||||
holder.avatar.recipients = listOf(createRecipient(contact))
|
||||
binding.avatar.recipients = listOf(createRecipient(contact))
|
||||
|
||||
holder.title.text = contact.name
|
||||
binding.title.text = contact.name
|
||||
|
||||
holder.subtitle.isVisible = false
|
||||
binding.subtitle.isVisible = false
|
||||
|
||||
holder.numbers.isVisible = true
|
||||
(holder.numbers.adapter as PhoneNumberAdapter).data = contact.numbers
|
||||
binding.numbers.isVisible = true
|
||||
(binding.numbers.adapter as PhoneNumberAdapter).data = contact.numbers
|
||||
}
|
||||
|
||||
private fun bindGroup(holder: QkViewHolder, group: ContactGroup, prev: ComposeItem?) {
|
||||
holder.index.isVisible = false
|
||||
private fun bindGroup(binding: ContactListItemBinding, group: ContactGroup, prev: ComposeItem?) {
|
||||
binding.index.isVisible = false
|
||||
|
||||
holder.icon.isVisible = prev !is ComposeItem.Group
|
||||
holder.icon.setImageResource(R.drawable.ic_people_black_24dp)
|
||||
binding.icon.isVisible = prev !is ComposeItem.Group
|
||||
binding.icon.setImageResource(R.drawable.ic_people_black_24dp)
|
||||
|
||||
holder.avatar.recipients = group.contacts.map(::createRecipient)
|
||||
binding.avatar.recipients = group.contacts.map(::createRecipient)
|
||||
|
||||
holder.title.text = group.title
|
||||
binding.title.text = group.title
|
||||
|
||||
holder.subtitle.isVisible = true
|
||||
holder.subtitle.text = group.contacts.joinToString(", ") { it.name }
|
||||
holder.subtitle.collapseEnabled = group.contacts.size > 1
|
||||
binding.subtitle.isVisible = true
|
||||
binding.subtitle.text = group.contacts.joinToString(", ") { it.name }
|
||||
binding.subtitle.collapseEnabled = group.contacts.size > 1
|
||||
|
||||
holder.numbers.isVisible = false
|
||||
binding.numbers.isVisible = false
|
||||
}
|
||||
|
||||
private fun bindPerson(holder: QkViewHolder, contact: Contact, prev: ComposeItem?) {
|
||||
holder.index.isVisible = true
|
||||
holder.index.text = if (contact.name.getOrNull(0)?.isLetter() == true) contact.name[0].toString() else "#"
|
||||
holder.index.isVisible = prev !is ComposeItem.Person ||
|
||||
private fun bindPerson(binding: ContactListItemBinding, contact: Contact, prev: ComposeItem?) {
|
||||
binding.index.isVisible = true
|
||||
binding.index.text = if (contact.name.getOrNull(0)?.isLetter() == true) contact.name[0].toString() else "#"
|
||||
binding.index.isVisible = prev !is ComposeItem.Person ||
|
||||
(contact.name[0].isLetter() && !contact.name[0].equals(prev.value.name[0], ignoreCase = true)) ||
|
||||
(!contact.name[0].isLetter() && prev.value.name[0].isLetter())
|
||||
|
||||
holder.icon.isVisible = false
|
||||
binding.icon.isVisible = false
|
||||
|
||||
holder.avatar.recipients = listOf(createRecipient(contact))
|
||||
binding.avatar.recipients = listOf(createRecipient(contact))
|
||||
|
||||
holder.title.text = contact.name
|
||||
binding.title.text = contact.name
|
||||
|
||||
holder.subtitle.isVisible = false
|
||||
binding.subtitle.isVisible = false
|
||||
|
||||
holder.numbers.isVisible = true
|
||||
(holder.numbers.adapter as PhoneNumberAdapter).data = contact.numbers
|
||||
binding.numbers.isVisible = true
|
||||
(binding.numbers.adapter as PhoneNumberAdapter).data = contact.numbers
|
||||
}
|
||||
|
||||
private fun createRecipient(contact: Contact): Recipient {
|
||||
|
||||
+13
-11
@@ -19,24 +19,26 @@
|
||||
package dev.octoshrimpy.quik.feature.compose.editing
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.widget.RelativeLayout
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.ContactChipDetailedBinding
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import dev.octoshrimpy.quik.model.Recipient
|
||||
import kotlinx.android.synthetic.main.contact_chip_detailed.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class DetailedChipView(context: Context) : RelativeLayout(context) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
private val binding: ContactChipDetailedBinding
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.contact_chip_detailed, this)
|
||||
binding = ContactChipDetailedBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
appComponent.inject(this)
|
||||
|
||||
setOnClickListener { hide() }
|
||||
@@ -48,15 +50,15 @@ class DetailedChipView(context: Context) : RelativeLayout(context) {
|
||||
}
|
||||
|
||||
fun setRecipient(recipient: Recipient) {
|
||||
avatar.setRecipient(recipient)
|
||||
name.text = recipient.contact?.name?.takeIf { it.isNotBlank() } ?: recipient.address
|
||||
info.text = recipient.address
|
||||
binding.avatar.setRecipient(recipient)
|
||||
binding.name.text = recipient.contact?.name?.takeIf { it.isNotBlank() } ?: recipient.address
|
||||
binding.info.text = recipient.address
|
||||
|
||||
colors.theme(recipient).let { theme ->
|
||||
card.setBackgroundTint(theme.theme)
|
||||
name.setTextColor(theme.textPrimary)
|
||||
info.setTextColor(theme.textTertiary)
|
||||
delete.setTint(theme.textPrimary)
|
||||
binding.card.setBackgroundTint(theme.theme)
|
||||
binding.name.setTextColor(theme.textPrimary)
|
||||
binding.info.setTextColor(theme.textTertiary)
|
||||
binding.delete.setTint(theme.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ class DetailedChipView(context: Context) : RelativeLayout(context) {
|
||||
}
|
||||
|
||||
fun setOnDeleteListener(listener: (View) -> Unit) {
|
||||
delete.setOnClickListener(listener)
|
||||
binding.delete.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-14
@@ -23,20 +23,18 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.base.QkBindingViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.extensions.forwardTouches
|
||||
import dev.octoshrimpy.quik.databinding.PhoneNumberListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.Optional
|
||||
import dev.octoshrimpy.quik.model.PhoneNumber
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.phone_number_list_item.*
|
||||
import kotlinx.android.synthetic.main.radio_preference_view.*
|
||||
import kotlinx.android.synthetic.main.radio_preference_view.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class PhoneNumberPickerAdapter @Inject constructor(
|
||||
private val context: Context
|
||||
) : QkAdapter<PhoneNumber, QkViewHolder>() {
|
||||
) : QkAdapter<PhoneNumber, QkBindingViewHolder<PhoneNumberListItemBinding>>() {
|
||||
|
||||
val selectedItemChanges: Subject<Optional<Long>> = BehaviorSubject.create()
|
||||
|
||||
@@ -48,25 +46,26 @@ class PhoneNumberPickerAdapter @Inject constructor(
|
||||
selectedItemChanges.onNext(Optional(value))
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkBindingViewHolder<PhoneNumberListItemBinding> {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.phone_number_list_item, parent, false)
|
||||
return QkViewHolder(view).apply {
|
||||
radioButton.forwardTouches(itemView)
|
||||
val binding = PhoneNumberListItemBinding.inflate(inflater, parent, false)
|
||||
return QkBindingViewHolder(binding).apply {
|
||||
binding.number.radioButton.forwardTouches(itemView)
|
||||
|
||||
view.setOnClickListener {
|
||||
binding.root.setOnClickListener {
|
||||
val phoneNumber = getItem(adapterPosition)
|
||||
selectedItem = phoneNumber.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: QkBindingViewHolder<PhoneNumberListItemBinding>, position: Int) {
|
||||
val binding = holder.binding
|
||||
val phoneNumber = getItem(position)
|
||||
|
||||
holder.number.radioButton.isChecked = phoneNumber.id == selectedItem
|
||||
holder.number.titleView.text = phoneNumber.address
|
||||
holder.number.summaryView.text = when (phoneNumber.isDefault) {
|
||||
binding.number.radioButton.isChecked = phoneNumber.id == selectedItem
|
||||
binding.number.titleView.text = phoneNumber.address
|
||||
binding.number.summaryView.text = when (phoneNumber.isDefault) {
|
||||
true -> context.getString(R.string.compose_number_picker_default, phoneNumber.type)
|
||||
false -> phoneNumber.type
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.withAlpha
|
||||
import dev.octoshrimpy.quik.common.widget.BubbleImageView
|
||||
import dev.octoshrimpy.quik.databinding.MmsAudioPreviewListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.isAudio
|
||||
import dev.octoshrimpy.quik.extensions.resourceExists
|
||||
import dev.octoshrimpy.quik.feature.compose.MessagesAdapter
|
||||
@@ -44,8 +45,6 @@ import dev.octoshrimpy.quik.util.GlideApp
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.mms_audio_preview_list_item.*
|
||||
import kotlinx.android.synthetic.main.mms_image_preview_list_item.thumbnail
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -69,33 +68,36 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
.subscribeOn(Schedulers.single())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
viewHolder?.seekBar?.progress = QkMediaPlayer.currentPosition
|
||||
viewHolder?.let { holder ->
|
||||
val binding = MmsAudioPreviewListItemBinding.bind(holder.itemView)
|
||||
binding.seekBar.progress = QkMediaPlayer.currentPosition
|
||||
}
|
||||
}
|
||||
.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private fun uiToPlaying(viewHolder: QkViewHolder) {
|
||||
viewHolder.seekBar.max = QkMediaPlayer.duration
|
||||
viewHolder.seekBar.isEnabled = true
|
||||
viewHolder.seekBar.progress = QkMediaPlayer.currentPosition
|
||||
viewHolder.playPause.setImageResource(R.drawable.exo_icon_pause)
|
||||
viewHolder.playPause.tag = QkMediaPlayer.PlayingState.Playing
|
||||
viewHolder.metadataTitle.isSelected = true // start marquee
|
||||
private fun uiToPlaying(binding: MmsAudioPreviewListItemBinding) {
|
||||
binding.seekBar.max = QkMediaPlayer.duration
|
||||
binding.seekBar.isEnabled = true
|
||||
binding.seekBar.progress = QkMediaPlayer.currentPosition
|
||||
binding.playPause.setImageResource(R.drawable.exo_icon_pause)
|
||||
binding.playPause.tag = QkMediaPlayer.PlayingState.Playing
|
||||
binding.metadataTitle.isSelected = true // start marquee
|
||||
}
|
||||
|
||||
private fun uiToPaused(viewHolder: QkViewHolder) {
|
||||
viewHolder.playPause.setImageResource(R.drawable.exo_icon_play)
|
||||
viewHolder.playPause.tag = QkMediaPlayer.PlayingState.Paused
|
||||
private fun uiToPaused(binding: MmsAudioPreviewListItemBinding) {
|
||||
binding.playPause.setImageResource(R.drawable.exo_icon_play)
|
||||
binding.playPause.tag = QkMediaPlayer.PlayingState.Paused
|
||||
}
|
||||
|
||||
private fun uiToStopped(viewHolder: QkViewHolder) {
|
||||
viewHolder.seekBar.progress = 0
|
||||
viewHolder.seekBar.max = 0
|
||||
viewHolder.seekBar.isEnabled = false
|
||||
viewHolder.playPause.setImageResource(R.drawable.exo_icon_play)
|
||||
viewHolder.playPause.tag = QkMediaPlayer.PlayingState.Stopped
|
||||
viewHolder.metadataTitle.isSelected = false // stop marquee
|
||||
private fun uiToStopped(binding: MmsAudioPreviewListItemBinding) {
|
||||
binding.seekBar.progress = 0
|
||||
binding.seekBar.max = 0
|
||||
binding.seekBar.isEnabled = false
|
||||
binding.playPause.setImageResource(R.drawable.exo_icon_play)
|
||||
binding.playPause.tag = QkMediaPlayer.PlayingState.Stopped
|
||||
binding.metadataTitle.isSelected = false // stop marquee
|
||||
}
|
||||
|
||||
override fun bindPart(
|
||||
@@ -105,16 +107,18 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
canGroupWithPrevious: Boolean,
|
||||
canGroupWithNext: Boolean,
|
||||
) {
|
||||
val binding = MmsAudioPreviewListItemBinding.bind(holder.itemView)
|
||||
|
||||
// click on background - passes back to compose view model
|
||||
holder.containerView.setOnClickListener { clicks.onNext(part.id) }
|
||||
holder.itemView.setOnClickListener { clicks.onNext(part.id) }
|
||||
|
||||
// play/pause button click handling
|
||||
holder.playPause.setOnClickListener {
|
||||
when (holder.playPause.tag) {
|
||||
binding.playPause.setOnClickListener {
|
||||
when (binding.playPause.tag) {
|
||||
QkMediaPlayer.PlayingState.Playing -> {
|
||||
if (audioState.partId == part.id) {
|
||||
QkMediaPlayer.pause()
|
||||
uiToPaused(holder)
|
||||
uiToPaused(binding)
|
||||
audioState.state = QkMediaPlayer.PlayingState.Paused
|
||||
|
||||
// stop progress bar update timer
|
||||
@@ -124,7 +128,7 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
QkMediaPlayer.PlayingState.Paused -> {
|
||||
if (audioState.partId == part.id) {
|
||||
QkMediaPlayer.start()
|
||||
uiToPlaying(holder)
|
||||
uiToPlaying(binding)
|
||||
audioState.state = QkMediaPlayer.PlayingState.Playing
|
||||
|
||||
// start progress bar update timer
|
||||
@@ -139,7 +143,7 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
// start media playing
|
||||
QkMediaPlayer.start()
|
||||
|
||||
uiToPlaying(holder)
|
||||
uiToPlaying(binding)
|
||||
|
||||
// set current view holder and part as active
|
||||
audioState.apply {
|
||||
@@ -155,8 +159,10 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
QkMediaPlayer.setOnCompletionListener { // also called on error because we don't have an onerrorlistener
|
||||
audioState.apply {
|
||||
// if this part is currently active, set it to stopped and inactive
|
||||
if ((partId == part.id) && (viewHolder != null))
|
||||
uiToStopped(viewHolder!!)
|
||||
if ((partId == part.id) && (viewHolder != null)) {
|
||||
val stoppedBinding = MmsAudioPreviewListItemBinding.bind(viewHolder!!.itemView)
|
||||
uiToStopped(stoppedBinding)
|
||||
}
|
||||
|
||||
state = QkMediaPlayer.PlayingState.Stopped
|
||||
partId = -1
|
||||
@@ -192,18 +198,18 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
if (!message.isMe())
|
||||
theme.theme
|
||||
else
|
||||
holder.containerView.context.resolveThemeColor(R.attr.bubbleColor)
|
||||
holder.itemView.context.resolveThemeColor(R.attr.bubbleColor)
|
||||
val primaryColor =
|
||||
if (!message.isMe())
|
||||
theme.textPrimary
|
||||
else
|
||||
holder.containerView.context.resolveThemeColor(android.R.attr.textColorPrimary)
|
||||
holder.itemView.context.resolveThemeColor(android.R.attr.textColorPrimary)
|
||||
|
||||
// sound wave
|
||||
holder.soundWave.setTint(primaryColor)
|
||||
binding.soundWave.setTint(primaryColor)
|
||||
|
||||
// seek bar
|
||||
holder.seekBar.apply {
|
||||
binding.seekBar.apply {
|
||||
setTint(secondaryColor)
|
||||
thumbTintList = ColorStateList.valueOf(secondaryColor)
|
||||
|
||||
@@ -219,15 +225,15 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
}
|
||||
|
||||
// playPause button
|
||||
holder.playPause. apply {
|
||||
binding.playPause.apply {
|
||||
if ((audioState.partId == part.id) &&
|
||||
(audioState.state == QkMediaPlayer.PlayingState.Playing))
|
||||
uiToPlaying(holder)
|
||||
uiToPlaying(binding)
|
||||
else if ((audioState.partId == part.id) &&
|
||||
(audioState.state == QkMediaPlayer.PlayingState.Paused))
|
||||
uiToPaused(holder)
|
||||
uiToPaused(binding)
|
||||
else
|
||||
uiToStopped(holder)
|
||||
uiToStopped(binding)
|
||||
|
||||
setTint(secondaryColor)
|
||||
setBackgroundTint(primaryColor)
|
||||
@@ -239,7 +245,7 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
setDataSource(context, part.getUri())
|
||||
|
||||
// metadata title
|
||||
holder.metadataTitle.apply {
|
||||
binding.metadataTitle.apply {
|
||||
text = extractMetadata(METADATA_KEY_TITLE)
|
||||
|
||||
if (text.isEmpty())
|
||||
@@ -252,7 +258,7 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
}
|
||||
|
||||
// bubble / embedded image
|
||||
holder.thumbnail.apply {
|
||||
binding.thumbnail.apply {
|
||||
bubbleStyle = when {
|
||||
!canGroupWithPrevious && canGroupWithNext ->
|
||||
if (message.isMe()) BubbleImageView.Style.OUT_FIRST else BubbleImageView.Style.IN_FIRST
|
||||
@@ -268,18 +274,18 @@ class AudioBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
|
||||
val embeddedPicture = embeddedPicture
|
||||
if (embeddedPicture == null) {
|
||||
holder.frame.layoutParams.height = (holder.frame.layoutParams.width / 2)
|
||||
binding.frame.layoutParams.height = (binding.frame.layoutParams.width / 2)
|
||||
setTint(secondaryColor)
|
||||
setImageResource(R.drawable.rectangle)
|
||||
} else {
|
||||
holder.frame.layoutParams.height = holder.frame.layoutParams.width
|
||||
binding.frame.layoutParams.height = binding.frame.layoutParams.width
|
||||
setTint(null)
|
||||
GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(embeddedPicture)
|
||||
.override(
|
||||
holder.frame.layoutParams.width,
|
||||
holder.frame.layoutParams.height
|
||||
binding.frame.layoutParams.width,
|
||||
binding.frame.layoutParams.height
|
||||
)
|
||||
.into(this)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.MmsFileListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.feature.compose.BubbleUtils
|
||||
import dev.octoshrimpy.quik.model.Message
|
||||
@@ -35,7 +36,6 @@ import dev.octoshrimpy.quik.util.tryOrNull
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.mms_file_list_item.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class FileBinder @Inject constructor(colors: Colors, private val context: Context) : PartBinder() {
|
||||
@@ -56,8 +56,9 @@ class FileBinder @Inject constructor(colors: Colors, private val context: Contex
|
||||
canGroupWithPrevious: Boolean,
|
||||
canGroupWithNext: Boolean
|
||||
) {
|
||||
val binding = MmsFileListItemBinding.bind(holder.itemView)
|
||||
BubbleUtils.getBubble(false, canGroupWithPrevious, canGroupWithNext, message.isMe())
|
||||
.let(holder.fileBackground::setBackgroundResource)
|
||||
.let(binding.fileBackground::setBackgroundResource)
|
||||
|
||||
tryOrNull(true) {
|
||||
Observable.just(part.getUri())
|
||||
@@ -76,21 +77,21 @@ class FileBinder @Inject constructor(colors: Colors, private val context: Contex
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { size -> holder.size.text = size }
|
||||
.subscribe { size -> binding.size.text = size }
|
||||
|
||||
holder.filename.text = part.getBestFilename()
|
||||
binding.filename.text = part.getBestFilename()
|
||||
}
|
||||
|
||||
if (!message.isMe()) {
|
||||
holder.fileBackground.setBackgroundTint(theme.theme)
|
||||
holder.icon.setTint(theme.textPrimary)
|
||||
holder.filename.setTextColor(theme.textPrimary)
|
||||
holder.size.setTextColor(theme.textTertiary)
|
||||
binding.fileBackground.setBackgroundTint(theme.theme)
|
||||
binding.icon.setTint(theme.textPrimary)
|
||||
binding.filename.setTextColor(theme.textPrimary)
|
||||
binding.size.setTextColor(theme.textTertiary)
|
||||
} else {
|
||||
holder.fileBackground.setBackgroundTint(holder.containerView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
holder.icon.setTint(holder.containerView.context.resolveThemeColor(android.R.attr.textColorSecondary))
|
||||
holder.filename.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
holder.size.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
binding.fileBackground.setBackgroundTint(holder.itemView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
binding.icon.setTint(holder.itemView.context.resolveThemeColor(android.R.attr.textColorSecondary))
|
||||
binding.filename.setTextColor(holder.itemView.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
binding.size.setTextColor(holder.itemView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.widget.BubbleImageView
|
||||
import dev.octoshrimpy.quik.databinding.MmsImagePreviewListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.isImage
|
||||
import dev.octoshrimpy.quik.extensions.isVideo
|
||||
import dev.octoshrimpy.quik.model.Message
|
||||
import dev.octoshrimpy.quik.model.MmsPart
|
||||
import dev.octoshrimpy.quik.util.GlideApp
|
||||
import dev.octoshrimpy.quik.util.tryOrNull
|
||||
import kotlinx.android.synthetic.main.mms_image_preview_list_item.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ImageBinder @Inject constructor(colors: Colors, private val context: Context) : PartBinder() {
|
||||
@@ -47,10 +47,11 @@ class ImageBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
canGroupWithPrevious: Boolean,
|
||||
canGroupWithNext: Boolean
|
||||
) {
|
||||
holder.video.setVisible(part.isVideo())
|
||||
holder.containerView.setOnClickListener { clicks.onNext(part.id) }
|
||||
val binding = MmsImagePreviewListItemBinding.bind(holder.itemView)
|
||||
binding.video.setVisible(part.isVideo())
|
||||
holder.itemView.setOnClickListener { clicks.onNext(part.id) }
|
||||
|
||||
holder.thumbnail.bubbleStyle = when {
|
||||
binding.thumbnail.bubbleStyle = when {
|
||||
!canGroupWithPrevious && canGroupWithNext -> if (message.isMe()) BubbleImageView.Style.OUT_FIRST else BubbleImageView.Style.IN_FIRST
|
||||
canGroupWithPrevious && canGroupWithNext -> if (message.isMe()) BubbleImageView.Style.OUT_MIDDLE else BubbleImageView.Style.IN_MIDDLE
|
||||
canGroupWithPrevious && !canGroupWithNext -> if (message.isMe()) BubbleImageView.Style.OUT_LAST else BubbleImageView.Style.IN_LAST
|
||||
@@ -58,7 +59,7 @@ class ImageBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
}
|
||||
|
||||
tryOrNull(true) {
|
||||
GlideApp.with(context).load(part.getUri()).fitCenter().into(holder.thumbnail)
|
||||
GlideApp.with(context).load(part.getUri()).fitCenter().into(binding.thumbnail)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import android.view.ViewGroup
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.widget.QkContextMenuRecyclerView
|
||||
import dev.octoshrimpy.quik.databinding.MessageListItemInBinding
|
||||
import dev.octoshrimpy.quik.databinding.MessageListItemOutBinding
|
||||
import dev.octoshrimpy.quik.extensions.isSmil
|
||||
import dev.octoshrimpy.quik.extensions.isText
|
||||
import dev.octoshrimpy.quik.feature.compose.BubbleUtils.canGroup
|
||||
@@ -31,7 +33,6 @@ import dev.octoshrimpy.quik.feature.compose.MessagesAdapter
|
||||
import dev.octoshrimpy.quik.model.Message
|
||||
import dev.octoshrimpy.quik.model.MmsPart
|
||||
import io.reactivex.Observable
|
||||
import kotlinx.android.synthetic.main.message_list_item_in.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -69,7 +70,12 @@ class PartsAdapter @Inject constructor(
|
||||
this.message = message
|
||||
this.previous = previous
|
||||
this.next = next
|
||||
this.bodyVisible = holder.body.visibility == View.VISIBLE
|
||||
// Get body visibility from appropriate binding based on message type
|
||||
this.bodyVisible = if (message.isMe()) {
|
||||
MessageListItemOutBinding.bind(holder.itemView).body.visibility == View.VISIBLE
|
||||
} else {
|
||||
MessageListItemInBinding.bind(holder.itemView).body.visibility == View.VISIBLE
|
||||
}
|
||||
this.data = message.parts.filter { !it.isSmil() && !it.isText() }
|
||||
this.audioState = audioState
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import dev.octoshrimpy.quik.common.util.extensions.getDisplayName
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.MmsVcardListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.isVCard
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.feature.compose.BubbleUtils
|
||||
@@ -37,7 +38,6 @@ import ezvcard.Ezvcard
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.mms_vcard_list_item.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VCardBinder @Inject constructor(colors: Colors, private val context: Context) : PartBinder() {
|
||||
@@ -54,10 +54,11 @@ class VCardBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
canGroupWithPrevious: Boolean,
|
||||
canGroupWithNext: Boolean
|
||||
) {
|
||||
val binding = MmsVcardListItemBinding.bind(holder.itemView)
|
||||
BubbleUtils.getBubble(false, canGroupWithPrevious, canGroupWithNext, message.isMe())
|
||||
.let(holder.vCardBackground::setBackgroundResource)
|
||||
.let(binding.vCardBackground::setBackgroundResource)
|
||||
|
||||
holder.containerView.setOnClickListener { clicks.onNext(part.id) }
|
||||
holder.itemView.setOnClickListener { clicks.onNext(part.id) }
|
||||
|
||||
tryOrNull(true) {
|
||||
Observable.just(part.getUri())
|
||||
@@ -67,21 +68,21 @@ class VCardBinder @Inject constructor(colors: Colors, private val context: Conte
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { displayName ->
|
||||
holder.name?.text = displayName
|
||||
holder.name.isVisible = displayName.isNotEmpty()
|
||||
binding.name.text = displayName
|
||||
binding.name.isVisible = displayName.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.isMe()) {
|
||||
holder.vCardBackground.setBackgroundTint(theme.theme)
|
||||
holder.vCardAvatar.setTint(theme.textPrimary)
|
||||
holder.name.setTextColor(theme.textPrimary)
|
||||
holder.label.setTextColor(theme.textTertiary)
|
||||
binding.vCardBackground.setBackgroundTint(theme.theme)
|
||||
binding.vCardAvatar.setTint(theme.textPrimary)
|
||||
binding.name.setTextColor(theme.textPrimary)
|
||||
binding.label.setTextColor(theme.textTertiary)
|
||||
} else {
|
||||
holder.vCardBackground.setBackgroundTint(holder.containerView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
holder.vCardAvatar.setTint(holder.containerView.context.resolveThemeColor(android.R.attr.textColorSecondary))
|
||||
holder.name.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
holder.label.setTextColor(holder.containerView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
binding.vCardBackground.setBackgroundTint(holder.itemView.context.resolveThemeColor(R.attr.bubbleColor))
|
||||
binding.vCardAvatar.setTint(holder.itemView.context.resolveThemeColor(android.R.attr.textColorSecondary))
|
||||
binding.name.setTextColor(holder.itemView.context.resolveThemeColor(android.R.attr.textColorPrimary))
|
||||
binding.label.setTextColor(holder.itemView.context.resolveThemeColor(android.R.attr.textColorTertiary))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import dev.octoshrimpy.quik.common.base.QkThemedActivity
|
||||
import dev.octoshrimpy.quik.common.util.extensions.hideKeyboard
|
||||
import dev.octoshrimpy.quik.common.util.extensions.showKeyboard
|
||||
import dev.octoshrimpy.quik.common.widget.QkDialog
|
||||
import dev.octoshrimpy.quik.databinding.ContactsActivityBinding
|
||||
import dev.octoshrimpy.quik.extensions.Optional
|
||||
import dev.octoshrimpy.quik.feature.compose.editing.ComposeItem
|
||||
import dev.octoshrimpy.quik.feature.compose.editing.ComposeItemAdapter
|
||||
@@ -43,7 +44,6 @@ import dev.octoshrimpy.quik.feature.compose.editing.PhoneNumberPickerAdapter
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.contacts_activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContactsActivity : QkThemedActivity(), ContactsContract {
|
||||
@@ -53,14 +53,16 @@ class ContactsActivity : QkThemedActivity(), ContactsContract {
|
||||
const val CHIPS_KEY = "chips"
|
||||
}
|
||||
|
||||
private lateinit var binding: ContactsActivityBinding
|
||||
|
||||
@Inject lateinit var contactsAdapter: ComposeItemAdapter
|
||||
@Inject lateinit var phoneNumberAdapter: PhoneNumberPickerAdapter
|
||||
@Inject lateinit var viewModelFactory: ViewModelFactory
|
||||
@Inject lateinit var navigator: Navigator
|
||||
|
||||
override val queryChangedIntent: Observable<CharSequence> by lazy { search.textChanges() }
|
||||
override val queryClearedIntent: Observable<*> by lazy { cancel.clicks() }
|
||||
override val queryEditorActionIntent: Observable<Int> by lazy { search.editorActions() }
|
||||
override val queryChangedIntent: Observable<CharSequence> by lazy { binding.search.textChanges() }
|
||||
override val queryClearedIntent: Observable<*> by lazy { binding.cancel.clicks() }
|
||||
override val queryEditorActionIntent: Observable<Int> by lazy { binding.search.editorActions() }
|
||||
override val composeItemPressedIntent: Subject<ComposeItem> by lazy { contactsAdapter.clicks }
|
||||
override val composeItemLongPressedIntent: Subject<ComposeItem> by lazy { contactsAdapter.longClicks }
|
||||
override val phoneNumberSelectedIntent: Subject<Optional<Long>> by lazy { phoneNumberAdapter.selectedItemChanges }
|
||||
@@ -83,11 +85,12 @@ class ContactsActivity : QkThemedActivity(), ContactsContract {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.contacts_activity)
|
||||
binding = ContactsActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
showBackButton(true)
|
||||
viewModel.bindView(this)
|
||||
|
||||
contacts.adapter = contactsAdapter
|
||||
binding.contacts.adapter = contactsAdapter
|
||||
|
||||
val callback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
@@ -98,7 +101,7 @@ class ContactsActivity : QkThemedActivity(), ContactsContract {
|
||||
}
|
||||
|
||||
override fun render(state: ContactsState) {
|
||||
cancel.isVisible = state.query.length > 1
|
||||
binding.cancel.isVisible = state.query.length > 1
|
||||
|
||||
contactsAdapter.data = state.composeItems
|
||||
|
||||
@@ -112,17 +115,17 @@ class ContactsActivity : QkThemedActivity(), ContactsContract {
|
||||
}
|
||||
|
||||
override fun clearQuery() {
|
||||
search.text = null
|
||||
binding.search.text = null
|
||||
}
|
||||
|
||||
override fun openKeyboard() {
|
||||
search.postDelayed({
|
||||
search.showKeyboard()
|
||||
binding.search.postDelayed({
|
||||
binding.search.showKeyboard()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
override fun finish(result: HashMap<String, String?>) {
|
||||
search.hideKeyboard()
|
||||
binding.search.hideKeyboard()
|
||||
val intent = Intent().putExtra(CHIPS_KEY, result)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
|
||||
+5
-4
@@ -23,20 +23,21 @@ import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import dagger.android.AndroidInjection
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkThemedActivity
|
||||
import kotlinx.android.synthetic.main.container_activity.*
|
||||
import dev.octoshrimpy.quik.databinding.ContainerActivityBinding
|
||||
|
||||
class ConversationInfoActivity : QkThemedActivity() {
|
||||
|
||||
private lateinit var binding: ContainerActivityBinding
|
||||
private lateinit var router: Router
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.container_activity)
|
||||
binding = ContainerActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
router = Conductor.attachRouter(this, container, savedInstanceState)
|
||||
router = Conductor.attachRouter(this, binding.container, savedInstanceState)
|
||||
if (!router.hasRootController()) {
|
||||
val threadId = intent.extras?.getLong("threadId") ?: 0L
|
||||
router.setRoot(RouterTransaction.with(ConversationInfoController(threadId)))
|
||||
|
||||
+52
-40
@@ -11,14 +11,14 @@ import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.databinding.ConversationInfoSettingsBinding
|
||||
import dev.octoshrimpy.quik.databinding.ConversationMediaListItemBinding
|
||||
import dev.octoshrimpy.quik.databinding.ConversationRecipientListItemBinding
|
||||
import dev.octoshrimpy.quik.extensions.isVideo
|
||||
import dev.octoshrimpy.quik.feature.conversationinfo.ConversationInfoItem.*
|
||||
import dev.octoshrimpy.quik.util.GlideApp
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.conversation_info_settings.*
|
||||
import kotlinx.android.synthetic.main.conversation_media_list_item.*
|
||||
import kotlinx.android.synthetic.main.conversation_recipient_list_item.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationInfoAdapter @Inject constructor(
|
||||
@@ -40,37 +40,46 @@ class ConversationInfoAdapter @Inject constructor(
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
0 -> QkViewHolder(inflater.inflate(R.layout.conversation_recipient_list_item, parent, false)).apply {
|
||||
itemView.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(recipientClicks::onNext)
|
||||
}
|
||||
0 -> {
|
||||
val binding = ConversationRecipientListItemBinding.inflate(inflater, parent, false)
|
||||
QkViewHolder(binding.root).apply {
|
||||
itemView.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(recipientClicks::onNext)
|
||||
}
|
||||
|
||||
itemView.setOnLongClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(recipientLongClicks::onNext)
|
||||
true
|
||||
}
|
||||
itemView.setOnLongClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(recipientLongClicks::onNext)
|
||||
true
|
||||
}
|
||||
|
||||
theme.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(themeClicks::onNext)
|
||||
binding.theme.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoRecipient
|
||||
item?.value?.id?.run(themeClicks::onNext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> QkViewHolder(inflater.inflate(R.layout.conversation_info_settings, parent, false)).apply {
|
||||
groupName.clicks().subscribe(nameClicks)
|
||||
notifications.clicks().subscribe(notificationClicks)
|
||||
markUnread.clicks().subscribe(markUnreadClicks)
|
||||
archive.clicks().subscribe(archiveClicks)
|
||||
block.clicks().subscribe(blockClicks)
|
||||
delete.clicks().subscribe(deleteClicks)
|
||||
1 -> {
|
||||
val binding = ConversationInfoSettingsBinding.inflate(inflater, parent, false)
|
||||
QkViewHolder(binding.root).apply {
|
||||
binding.groupName.clicks().subscribe(nameClicks)
|
||||
binding.notifications.clicks().subscribe(notificationClicks)
|
||||
binding.markUnread.clicks().subscribe(markUnreadClicks)
|
||||
binding.archive.clicks().subscribe(archiveClicks)
|
||||
binding.block.clicks().subscribe(blockClicks)
|
||||
binding.delete.clicks().subscribe(deleteClicks)
|
||||
}
|
||||
}
|
||||
|
||||
2 -> QkViewHolder(inflater.inflate(R.layout.conversation_media_list_item, parent, false)).apply {
|
||||
itemView.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoMedia
|
||||
item?.value?.id?.run(mediaClicks::onNext)
|
||||
2 -> {
|
||||
val binding = ConversationMediaListItemBinding.inflate(inflater, parent, false)
|
||||
QkViewHolder(binding.root).apply {
|
||||
itemView.setOnClickListener {
|
||||
val item = getItem(adapterPosition) as? ConversationInfoMedia
|
||||
item?.value?.id?.run(mediaClicks::onNext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,46 +90,49 @@ class ConversationInfoAdapter @Inject constructor(
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
when (val item = getItem(position)) {
|
||||
is ConversationInfoRecipient -> {
|
||||
val binding = ConversationRecipientListItemBinding.bind(holder.itemView)
|
||||
val recipient = item.value
|
||||
holder.avatar.setRecipient(recipient)
|
||||
binding.avatar.setRecipient(recipient)
|
||||
|
||||
holder.name.text = recipient.contact?.name ?: recipient.address
|
||||
binding.name.text = recipient.contact?.name ?: recipient.address
|
||||
|
||||
holder.address.text = recipient.address
|
||||
holder.address.setVisible(recipient.contact != null)
|
||||
binding.address.text = recipient.address
|
||||
binding.address.setVisible(recipient.contact != null)
|
||||
|
||||
holder.add.setVisible(recipient.contact == null)
|
||||
binding.add.setVisible(recipient.contact == null)
|
||||
|
||||
val theme = colors.theme(recipient)
|
||||
holder.theme.setTint(theme.theme)
|
||||
binding.theme.setTint(theme.theme)
|
||||
}
|
||||
|
||||
is ConversationInfoSettings -> {
|
||||
holder.groupName.summary = item.name
|
||||
val binding = ConversationInfoSettingsBinding.bind(holder.itemView)
|
||||
binding.groupName.summary = item.name
|
||||
|
||||
holder.notifications.isEnabled = !item.blocked
|
||||
binding.notifications.isEnabled = !item.blocked
|
||||
|
||||
holder.archive.isEnabled = !item.blocked
|
||||
holder.archive.title = context.getString(when (item.archived) {
|
||||
binding.archive.isEnabled = !item.blocked
|
||||
binding.archive.title = context.getString(when (item.archived) {
|
||||
true -> R.string.info_unarchive
|
||||
false -> R.string.info_archive
|
||||
})
|
||||
|
||||
holder.block.title = context.getString(when (item.blocked) {
|
||||
binding.block.title = context.getString(when (item.blocked) {
|
||||
true -> R.string.info_unblock
|
||||
false -> R.string.info_block
|
||||
})
|
||||
}
|
||||
|
||||
is ConversationInfoMedia -> {
|
||||
val binding = ConversationMediaListItemBinding.bind(holder.itemView)
|
||||
val part = item.value
|
||||
|
||||
GlideApp.with(context)
|
||||
.load(part.getUri())
|
||||
.fitCenter()
|
||||
.into(holder.thumbnail)
|
||||
.into(binding.thumbnail)
|
||||
|
||||
holder.video.isVisible = part.isVideo()
|
||||
binding.video.isVisible = part.isVideo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-8
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.conversationinfo
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
@@ -30,6 +32,7 @@ import dev.octoshrimpy.quik.common.QkChangeHandler
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.util.extensions.scrapViews
|
||||
import dev.octoshrimpy.quik.common.widget.TextInputDialog
|
||||
import dev.octoshrimpy.quik.databinding.ConversationInfoControllerBinding
|
||||
import dev.octoshrimpy.quik.feature.blocking.BlockingDialog
|
||||
import dev.octoshrimpy.quik.feature.conversationinfo.injection.ConversationInfoModule
|
||||
import dev.octoshrimpy.quik.feature.themepicker.ThemePickerController
|
||||
@@ -37,12 +40,14 @@ import dev.octoshrimpy.quik.injection.appComponent
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.conversation_info_controller.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationInfoController(
|
||||
val threadId: Long = 0
|
||||
) : QkController<ConversationInfoView, ConversationInfoState, ConversationInfoPresenter>(), ConversationInfoView {
|
||||
) : QkController<ConversationInfoControllerBinding, ConversationInfoView, ConversationInfoState, ConversationInfoPresenter>(), ConversationInfoView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): ConversationInfoControllerBinding =
|
||||
ConversationInfoControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject override lateinit var presenter: ConversationInfoPresenter
|
||||
@Inject lateinit var blockingDialog: BlockingDialog
|
||||
@@ -62,14 +67,12 @@ class ConversationInfoController(
|
||||
.conversationInfoModule(ConversationInfoModule(this))
|
||||
.build()
|
||||
.inject(this)
|
||||
|
||||
layoutRes = R.layout.conversation_info_controller
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.addItemDecoration(GridSpacingItemDecoration(adapter, activity!!))
|
||||
recyclerView.layoutManager = GridLayoutManager(activity, 3).apply {
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.recyclerView.addItemDecoration(GridSpacingItemDecoration(adapter, activity!!))
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(activity, 3).apply {
|
||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int = if (adapter.getItemViewType(position) == 2) 1 else 3
|
||||
}
|
||||
@@ -77,7 +80,7 @@ class ConversationInfoController(
|
||||
|
||||
themedActivity?.theme
|
||||
?.autoDisposable(scope())
|
||||
?.subscribe { recyclerView.scrapViews() }
|
||||
?.subscribe { binding.recyclerView.scrapViews() }
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
|
||||
+29
-29
@@ -27,18 +27,17 @@ import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.Navigator
|
||||
import dev.octoshrimpy.quik.common.base.QkBindingViewHolder
|
||||
import dev.octoshrimpy.quik.common.base.QkRealmAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.DateFormatter
|
||||
import dev.octoshrimpy.quik.common.util.extensions.resolveThemeColor
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.ConversationListItemBinding
|
||||
import dev.octoshrimpy.quik.model.Conversation
|
||||
import dev.octoshrimpy.quik.repository.ScheduledMessageRepository
|
||||
import dev.octoshrimpy.quik.util.PhoneNumberUtils
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.android.synthetic.main.conversation_list_item.*
|
||||
import kotlinx.android.synthetic.main.conversation_list_item.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationsAdapter @Inject constructor(
|
||||
@@ -48,7 +47,7 @@ class ConversationsAdapter @Inject constructor(
|
||||
private val scheduledMessageRepo: ScheduledMessageRepository,
|
||||
private val navigator: Navigator,
|
||||
private val phoneNumberUtils: PhoneNumberUtils
|
||||
) : QkRealmAdapter<Conversation, QkViewHolder>() {
|
||||
) : QkRealmAdapter<Conversation, QkBindingViewHolder<ConversationListItemBinding>>() {
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
init {
|
||||
@@ -56,44 +55,45 @@ class ConversationsAdapter @Inject constructor(
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkBindingViewHolder<ConversationListItemBinding> {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val view = layoutInflater.inflate(R.layout.conversation_list_item, parent, false)
|
||||
val binding = ConversationListItemBinding.inflate(layoutInflater, parent, false)
|
||||
|
||||
if (viewType == 1) {
|
||||
val textColorPrimary = parent.context.resolveThemeColor(android.R.attr.textColorPrimary)
|
||||
|
||||
view.title.setTypeface(view.title.typeface, Typeface.BOLD)
|
||||
binding.title.setTypeface(binding.title.typeface, Typeface.BOLD)
|
||||
|
||||
view.snippet.setTypeface(view.snippet.typeface, Typeface.BOLD)
|
||||
view.snippet.setTextColor(textColorPrimary)
|
||||
view.snippet.maxLines = 5
|
||||
binding.snippet.setTypeface(binding.snippet.typeface, Typeface.BOLD)
|
||||
binding.snippet.setTextColor(textColorPrimary)
|
||||
binding.snippet.maxLines = 5
|
||||
|
||||
view.unread.isVisible = true
|
||||
binding.unread.isVisible = true
|
||||
|
||||
view.date.setTypeface(view.date.typeface, Typeface.BOLD)
|
||||
view.date.setTextColor(textColorPrimary)
|
||||
binding.date.setTypeface(binding.date.typeface, Typeface.BOLD)
|
||||
binding.date.setTextColor(textColorPrimary)
|
||||
}
|
||||
|
||||
return QkViewHolder(view).apply {
|
||||
view.setOnClickListener {
|
||||
return QkBindingViewHolder(binding).apply {
|
||||
binding.root.setOnClickListener {
|
||||
val conversation = getItem(adapterPosition) ?: return@setOnClickListener
|
||||
when (toggleSelection(conversation.id, false)) {
|
||||
true -> view.isActivated = isSelected(conversation.id)
|
||||
true -> binding.root.isActivated = isSelected(conversation.id)
|
||||
false -> navigator.showConversation(conversation.id)
|
||||
}
|
||||
}
|
||||
view.setOnLongClickListener {
|
||||
binding.root.setOnLongClickListener {
|
||||
val conversation = getItem(adapterPosition) ?: return@setOnLongClickListener true
|
||||
toggleSelection(conversation.id)
|
||||
view.isActivated = isSelected(conversation.id)
|
||||
binding.root.isActivated = isSelected(conversation.id)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: QkBindingViewHolder<ConversationListItemBinding>, position: Int) {
|
||||
val conversation = getItem(position) ?: return
|
||||
val binding = holder.binding
|
||||
|
||||
// If the last message wasn't incoming, then the colour doesn't really matter anyway
|
||||
val lastMessage = conversation.lastMessage
|
||||
@@ -105,22 +105,22 @@ class ConversationsAdapter @Inject constructor(
|
||||
}
|
||||
val theme = colors.theme(recipient).theme
|
||||
|
||||
holder.containerView.isActivated = isSelected(conversation.id)
|
||||
holder.itemView.isActivated = isSelected(conversation.id)
|
||||
|
||||
holder.avatars.recipients = conversation.recipients
|
||||
holder.title.collapseEnabled = conversation.recipients.size > 1
|
||||
holder.title.text = buildSpannedString {
|
||||
binding.avatars.recipients = conversation.recipients
|
||||
binding.title.collapseEnabled = conversation.recipients.size > 1
|
||||
binding.title.text = buildSpannedString {
|
||||
append(conversation.getTitle())
|
||||
}
|
||||
holder.date.text = conversation.date.takeIf { it > 0 }?.let(dateFormatter::getConversationTimestamp)
|
||||
holder.snippet.text = when {
|
||||
binding.date.text = conversation.date.takeIf { it > 0 }?.let(dateFormatter::getConversationTimestamp)
|
||||
binding.snippet.text = when {
|
||||
conversation.draft.isNotEmpty() -> context.getString(R.string.main_sender_draft, conversation.draft)
|
||||
conversation.me -> context.getString(R.string.main_sender_you, conversation.snippet)
|
||||
else -> conversation.snippet
|
||||
}
|
||||
|
||||
// Make the preview in italics if draft
|
||||
if (conversation.draft.isNotEmpty()) holder.snippet.setTypeface(null, Typeface.ITALIC)
|
||||
if (conversation.draft.isNotEmpty()) binding.snippet.setTypeface(null, Typeface.ITALIC)
|
||||
|
||||
// Get Scheduled Messages
|
||||
val disposable = scheduledMessageRepo
|
||||
@@ -128,12 +128,12 @@ class ConversationsAdapter @Inject constructor(
|
||||
.asFlowable()
|
||||
.toObservable()
|
||||
.subscribe { messages ->
|
||||
holder.scheduled.isVisible = messages.isNotEmpty()
|
||||
binding.scheduled.isVisible = messages.isNotEmpty()
|
||||
}
|
||||
disposables.add(disposable)
|
||||
|
||||
holder.pinned.isVisible = conversation.pinned
|
||||
holder.unread.setTint(theme)
|
||||
binding.pinned.isVisible = conversation.pinned
|
||||
binding.unread.setTint(theme)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
|
||||
@@ -34,15 +34,17 @@ import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkActivity
|
||||
import dev.octoshrimpy.quik.common.util.DateFormatter
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.databinding.GalleryActivityBinding
|
||||
import dev.octoshrimpy.quik.model.MmsPart
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.gallery_activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class GalleryActivity : QkActivity(), GalleryView {
|
||||
|
||||
private lateinit var binding: GalleryActivityBinding
|
||||
|
||||
@Inject lateinit var dateFormatter: DateFormatter
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
@Inject lateinit var pagerAdapter: GalleryPagerAdapter
|
||||
@@ -57,12 +59,13 @@ class GalleryActivity : QkActivity(), GalleryView {
|
||||
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.gallery_activity)
|
||||
binding = GalleryActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
showBackButton(true)
|
||||
viewModel.bindView(this)
|
||||
|
||||
pager.adapter = pagerAdapter
|
||||
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
binding.pager.adapter = pagerAdapter
|
||||
binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
this@GalleryActivity.onPageSelected(position)
|
||||
}
|
||||
@@ -74,7 +77,7 @@ class GalleryActivity : QkActivity(), GalleryView {
|
||||
?.indexOfFirst { part -> part.id == partId }
|
||||
?.let { index ->
|
||||
onPageSelected(index)
|
||||
pager.setCurrentItem(index, false)
|
||||
binding.pager.setCurrentItem(index, false)
|
||||
pagerAdapter.unregisterAdapterDataObserver(this)
|
||||
}
|
||||
}
|
||||
@@ -82,15 +85,15 @@ class GalleryActivity : QkActivity(), GalleryView {
|
||||
}
|
||||
|
||||
fun onPageSelected(position: Int) {
|
||||
toolbarSubtitle.text = pagerAdapter.getItem(position)?.messages?.firstOrNull()?.date
|
||||
binding.toolbarSubtitle.text = pagerAdapter.getItem(position)?.messages?.firstOrNull()?.date
|
||||
?.let(dateFormatter::getDetailedTimestamp)
|
||||
toolbarSubtitle.isVisible = toolbarTitle.text.isNotBlank()
|
||||
binding.toolbarSubtitle.isVisible = binding.toolbarTitle.text.isNotBlank()
|
||||
|
||||
pagerAdapter.getItem(position)?.run(pageChangedSubject::onNext)
|
||||
}
|
||||
|
||||
override fun render(state: GalleryState) {
|
||||
toolbar.setVisible(state.navigationVisible)
|
||||
toolbar?.setVisible(state.navigationVisible)
|
||||
|
||||
title = state.title
|
||||
pagerAdapter.updateData(state.parts)
|
||||
|
||||
@@ -33,15 +33,14 @@ import com.google.android.mms.ContentType
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkRealmAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.databinding.GalleryImagePageBinding
|
||||
import dev.octoshrimpy.quik.databinding.GalleryVideoPageBinding
|
||||
import dev.octoshrimpy.quik.extensions.isImage
|
||||
import dev.octoshrimpy.quik.extensions.isVideo
|
||||
import dev.octoshrimpy.quik.model.MmsPart
|
||||
import dev.octoshrimpy.quik.util.GlideApp
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.gallery_image_page.*
|
||||
import kotlinx.android.synthetic.main.gallery_image_page.view.*
|
||||
import kotlinx.android.synthetic.main.gallery_video_page.*
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -61,25 +60,26 @@ class GalleryPagerAdapter @Inject constructor(private val context: Context) : Qk
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return QkViewHolder(when (viewType) {
|
||||
VIEW_TYPE_IMAGE -> inflater.inflate(R.layout.gallery_image_page, parent, false).apply {
|
||||
|
||||
VIEW_TYPE_IMAGE -> {
|
||||
val binding = GalleryImagePageBinding.inflate(inflater, parent, false)
|
||||
// When calling the public setter, it doesn't allow the midscale to be the same as the
|
||||
// maxscale or the minscale. We don't want 3 levels and we don't want to modify the library
|
||||
// so let's celebrate the invention of reflection!
|
||||
image.attacher.run {
|
||||
binding.image.attacher.run {
|
||||
javaClass.getDeclaredField("mMinScale").run {
|
||||
isAccessible = true
|
||||
setFloat(image.attacher, 1f)
|
||||
setFloat(binding.image.attacher, 1f)
|
||||
}
|
||||
javaClass.getDeclaredField("mMidScale").run {
|
||||
isAccessible = true
|
||||
setFloat(image.attacher, 1f)
|
||||
setFloat(binding.image.attacher, 1f)
|
||||
}
|
||||
javaClass.getDeclaredField("mMaxScale").run {
|
||||
isAccessible = true
|
||||
setFloat(image.attacher, 3f)
|
||||
setFloat(binding.image.attacher, 3f)
|
||||
}
|
||||
}
|
||||
binding.root
|
||||
}
|
||||
|
||||
VIEW_TYPE_VIDEO -> inflater.inflate(R.layout.gallery_video_page, parent, false)
|
||||
@@ -93,25 +93,27 @@ class GalleryPagerAdapter @Inject constructor(private val context: Context) : Qk
|
||||
val part = getItem(position) ?: return
|
||||
when (getItemViewType(position)) {
|
||||
VIEW_TYPE_IMAGE -> {
|
||||
val binding = GalleryImagePageBinding.bind(holder.itemView)
|
||||
// We need to explicitly request a gif from glide for animations to work
|
||||
when (part.getUri().let(contentResolver::getType)) {
|
||||
ContentType.IMAGE_GIF -> GlideApp.with(context)
|
||||
.asGif()
|
||||
.load(part.getUri())
|
||||
.into(holder.image)
|
||||
.into(binding.image)
|
||||
|
||||
else -> GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(part.getUri())
|
||||
.into(holder.image)
|
||||
.into(binding.image)
|
||||
}
|
||||
}
|
||||
|
||||
VIEW_TYPE_VIDEO -> {
|
||||
val binding = GalleryVideoPageBinding.bind(holder.itemView)
|
||||
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory(null)
|
||||
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
|
||||
val exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector)
|
||||
holder.video.player = exoPlayer
|
||||
binding.video.player = exoPlayer
|
||||
exoPlayers.add(exoPlayer)
|
||||
|
||||
val dataSourceFactory = DefaultDataSourceFactory(context, Util.getUserAgent(context, "QUIK"))
|
||||
|
||||
@@ -57,6 +57,9 @@ import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.widget.TextInputDialog
|
||||
import dev.octoshrimpy.quik.feature.blocking.BlockingDialog
|
||||
import dev.octoshrimpy.quik.databinding.MainActivityBinding
|
||||
import dev.octoshrimpy.quik.databinding.MainPermissionHintBinding
|
||||
import dev.octoshrimpy.quik.databinding.MainSyncingBinding
|
||||
import dev.octoshrimpy.quik.feature.changelog.ChangelogDialog
|
||||
import dev.octoshrimpy.quik.feature.conversations.ConversationItemTouchCallback
|
||||
import dev.octoshrimpy.quik.feature.conversations.ConversationsAdapter
|
||||
@@ -66,10 +69,6 @@ import io.reactivex.Observable
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.drawer_view.*
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.android.synthetic.main.main_permission_hint.*
|
||||
import kotlinx.android.synthetic.main.main_syncing.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MainActivity : QkThemedActivity(), MainView {
|
||||
@@ -83,32 +82,36 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
@Inject lateinit var itemTouchCallback: ConversationItemTouchCallback
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
private lateinit var binding: MainActivityBinding
|
||||
private lateinit var snackbarBinding: MainPermissionHintBinding
|
||||
private lateinit var syncingBinding: MainSyncingBinding
|
||||
|
||||
override val onNewIntentIntent: Subject<Intent> = PublishSubject.create()
|
||||
override val activityResumedIntent: Subject<Boolean> = PublishSubject.create()
|
||||
override val queryChangedIntent by lazy { toolbarSearch.textChanges() }
|
||||
override val composeIntent by lazy { compose.clicks() }
|
||||
override val queryChangedIntent by lazy { binding.toolbarSearch.textChanges() }
|
||||
override val composeIntent by lazy { binding.compose.clicks() }
|
||||
override val drawerToggledIntent: Observable<Boolean> by lazy {
|
||||
drawerLayout.drawerOpen(Gravity.START)
|
||||
binding.drawerLayout.drawerOpen(Gravity.START)
|
||||
}
|
||||
override val homeIntent: Subject<Unit> = PublishSubject.create()
|
||||
override val navigationIntent: Observable<NavItem> by lazy {
|
||||
Observable.merge(listOf(
|
||||
backPressedSubject,
|
||||
inbox.clicks().map { NavItem.INBOX },
|
||||
archived.clicks().map { NavItem.ARCHIVED },
|
||||
backup.clicks().map { NavItem.BACKUP },
|
||||
scheduled.clicks().map { NavItem.SCHEDULED },
|
||||
blocking.clicks().map { NavItem.BLOCKING },
|
||||
messageUtils.clicks().map { NavItem.MESSAGE_UTILS },
|
||||
settings.clicks().map { NavItem.SETTINGS },
|
||||
binding.drawer.inbox.clicks().map { NavItem.INBOX },
|
||||
binding.drawer.archived.clicks().map { NavItem.ARCHIVED },
|
||||
binding.drawer.backup.clicks().map { NavItem.BACKUP },
|
||||
binding.drawer.scheduled.clicks().map { NavItem.SCHEDULED },
|
||||
binding.drawer.blocking.clicks().map { NavItem.BLOCKING },
|
||||
binding.drawer.messageUtils.clicks().map { NavItem.MESSAGE_UTILS },
|
||||
binding.drawer.settings.clicks().map { NavItem.SETTINGS },
|
||||
// plus.clicks().map { NavItem.PLUS },
|
||||
// help.clicks().map { NavItem.HELP },
|
||||
invite.clicks().map { NavItem.INVITE }))
|
||||
binding.drawer.invite.clicks().map { NavItem.INVITE }))
|
||||
}
|
||||
override val optionsItemIntent: Subject<Int> = PublishSubject.create()
|
||||
// override val plusBannerIntent by lazy { plusBanner.clicks() }
|
||||
override val dismissRatingIntent by lazy { rateDismiss.clicks() }
|
||||
override val rateIntent by lazy { rateOkay.clicks() }
|
||||
override val dismissRatingIntent by lazy { binding.drawer.rateDismiss.clicks() }
|
||||
override val rateIntent by lazy { binding.drawer.rateOkay.clicks() }
|
||||
override val conversationsSelectedIntent by lazy { conversationsAdapter.selectionChanges }
|
||||
override val confirmDeleteIntent: Subject<List<Long>> = PublishSubject.create()
|
||||
override val renameConversationIntent: Subject<String> = PublishSubject.create()
|
||||
@@ -123,52 +126,49 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
private val toggle by lazy {
|
||||
ActionBarDrawerToggle(
|
||||
this,
|
||||
drawerLayout,
|
||||
toolbar,
|
||||
binding.drawerLayout,
|
||||
binding.toolbar,
|
||||
R.string.main_drawer_open_cd,
|
||||
0
|
||||
)
|
||||
}
|
||||
private val itemTouchHelper by lazy { ItemTouchHelper(itemTouchCallback) }
|
||||
private val progressAnimator by lazy {
|
||||
ObjectAnimator.ofInt(syncingProgress, "progress", 0, 0)
|
||||
ObjectAnimator.ofInt(syncingBinding.syncingProgress, "progress", 0, 0)
|
||||
}
|
||||
private val changelogDialog by lazy { ChangelogDialog(this) }
|
||||
private val snackbar by lazy { findViewById<View>(R.id.snackbar) }
|
||||
private val syncing by lazy { findViewById<View>(R.id.syncing) }
|
||||
private val backPressedSubject: Subject<NavItem> = PublishSubject.create()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.main_activity)
|
||||
binding = MainActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
viewModel.bindView(this)
|
||||
onNewIntentIntent.onNext(intent)
|
||||
|
||||
(snackbar as? ViewStub)?.setOnInflateListener { _, _ ->
|
||||
snackbarButton.clicks()
|
||||
.autoDisposable(scope(Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe(snackbarButtonIntent)
|
||||
snackbarBinding = MainPermissionHintBinding.bind(binding.snackbar.inflate()).also {
|
||||
it.snackbarButton.clicks()
|
||||
.autoDisposable(scope(Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe(snackbarButtonIntent)
|
||||
}
|
||||
|
||||
(syncing as? ViewStub)?.setOnInflateListener { _, _ ->
|
||||
syncingProgress?.let {
|
||||
it.progressTintList = ColorStateList.valueOf(theme.blockingFirst().theme)
|
||||
it.indeterminateTintList = ColorStateList.valueOf(theme.blockingFirst().theme)
|
||||
}
|
||||
syncingBinding = MainSyncingBinding.bind(binding.syncing.inflate()).also {
|
||||
it.syncingProgress.progressTintList = ColorStateList.valueOf(theme.blockingFirst().theme)
|
||||
it.syncingProgress.indeterminateTintList = ColorStateList.valueOf(theme.blockingFirst().theme)
|
||||
}
|
||||
|
||||
toggle.syncState()
|
||||
toolbar.setNavigationOnClickListener {
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
dismissKeyboard()
|
||||
homeIntent.onNext(Unit)
|
||||
}
|
||||
|
||||
itemTouchCallback.adapter = conversationsAdapter
|
||||
conversationsAdapter.autoScrollToStart(recyclerView)
|
||||
conversationsAdapter.autoScrollToStart(binding.recyclerView)
|
||||
|
||||
// Don't allow clicks to pass through the drawer layout
|
||||
drawer.clicks().autoDisposable(scope()).subscribe()
|
||||
binding.drawer.root.clicks().autoDisposable(scope()).subscribe()
|
||||
|
||||
// Set the theme color tint to the recyclerView, progressbar, and FAB
|
||||
theme
|
||||
@@ -183,23 +183,23 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
resolveThemeColor(android.R.attr.textColorSecondary)
|
||||
))
|
||||
.let { tintList ->
|
||||
inboxIcon.imageTintList = tintList
|
||||
archivedIcon.imageTintList = tintList
|
||||
binding.drawer.inboxIcon.imageTintList = tintList
|
||||
binding.drawer.archivedIcon.imageTintList = tintList
|
||||
}
|
||||
|
||||
// Miscellaneous views
|
||||
listOf(plusBadge1, plusBadge2).forEach { badge ->
|
||||
listOf(binding.drawer.plusBadge1, binding.drawer.plusBadge2).forEach { badge ->
|
||||
badge.setBackgroundTint(theme.theme)
|
||||
badge.setTextColor(theme.textPrimary)
|
||||
}
|
||||
syncingProgress?.progressTintList = ColorStateList.valueOf(theme.theme)
|
||||
syncingProgress?.indeterminateTintList = ColorStateList.valueOf(theme.theme)
|
||||
plusIcon.setTint(theme.theme)
|
||||
rateIcon.setTint(theme.theme)
|
||||
compose.setBackgroundTint(theme.theme)
|
||||
syncingBinding.syncingProgress.progressTintList = ColorStateList.valueOf(theme.theme)
|
||||
syncingBinding.syncingProgress.indeterminateTintList = ColorStateList.valueOf(theme.theme)
|
||||
binding.drawer.plusIcon.setTint(theme.theme)
|
||||
binding.drawer.rateIcon.setTint(theme.theme)
|
||||
binding.compose.setBackgroundTint(theme.theme)
|
||||
|
||||
// Set the FAB compose icon color
|
||||
compose.setTint(theme.textPrimary)
|
||||
binding.compose.setTint(theme.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,13 +239,13 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
else -> 0
|
||||
}
|
||||
|
||||
toolbarSearch.setVisible(state.page is Inbox &&
|
||||
binding.toolbarSearch.setVisible(state.page is Inbox &&
|
||||
state.page.selected == 0 ||
|
||||
state.page is Searching
|
||||
)
|
||||
toolbarTitle.setVisible(toolbarSearch.visibility != View.VISIBLE)
|
||||
binding.toolbarTitle.setVisible(binding.toolbarSearch.visibility != View.VISIBLE)
|
||||
|
||||
toolbar.menu.apply {
|
||||
binding.toolbar.menu.apply {
|
||||
findItem(R.id.select_all)?.isVisible =
|
||||
(conversationsAdapter.itemCount > 1) && selectedConversations != 0
|
||||
findItem(R.id.archive)?.isVisible =
|
||||
@@ -264,36 +264,36 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
findItem(R.id.rename)?.isVisible = selectedConversations == 1
|
||||
}
|
||||
|
||||
listOf(plusBadge1, plusBadge2).forEach { badge ->
|
||||
listOf(binding.drawer.plusBadge1, binding.drawer.plusBadge2).forEach { badge ->
|
||||
badge.isVisible = drawerBadgesExperiment.variant && !state.upgraded
|
||||
}
|
||||
// plus.isVisible = state.upgraded
|
||||
plusBanner.isVisible = !state.upgraded
|
||||
rateLayout.setVisible(state.showRating)
|
||||
binding.drawer.plusBanner.isVisible = !state.upgraded
|
||||
binding.drawer.rateLayout.setVisible(state.showRating)
|
||||
|
||||
compose.setVisible(state.page is Inbox || state.page is Archived)
|
||||
conversationsAdapter.emptyView = empty.takeIf {
|
||||
binding.compose.setVisible(state.page is Inbox || state.page is Archived)
|
||||
conversationsAdapter.emptyView = binding.empty.takeIf {
|
||||
state.page is Inbox || state.page is Archived
|
||||
}
|
||||
searchAdapter.emptyView = empty.takeIf { state.page is Searching }
|
||||
searchAdapter.emptyView = binding.empty.takeIf { state.page is Searching }
|
||||
|
||||
when (state.page) {
|
||||
is Inbox -> {
|
||||
showBackButton(state.page.selected > 0)
|
||||
title = getString(R.string.main_title_selected, state.page.selected)
|
||||
if (recyclerView.adapter !== conversationsAdapter)
|
||||
recyclerView.adapter = conversationsAdapter
|
||||
if (binding.recyclerView.adapter !== conversationsAdapter)
|
||||
binding.recyclerView.adapter = conversationsAdapter
|
||||
conversationsAdapter.updateData(state.page.data)
|
||||
itemTouchHelper.attachToRecyclerView(recyclerView)
|
||||
empty.setText(R.string.inbox_empty_text)
|
||||
itemTouchHelper.attachToRecyclerView(binding.recyclerView)
|
||||
binding.empty.setText(R.string.inbox_empty_text)
|
||||
}
|
||||
|
||||
is Searching -> {
|
||||
showBackButton(true)
|
||||
if (recyclerView.adapter !== searchAdapter) recyclerView.adapter = searchAdapter
|
||||
if (binding.recyclerView.adapter !== searchAdapter) binding.recyclerView.adapter = searchAdapter
|
||||
searchAdapter.data = state.page.data ?: listOf()
|
||||
itemTouchHelper.attachToRecyclerView(null)
|
||||
empty.setText(R.string.inbox_search_empty_text)
|
||||
binding.empty.setText(R.string.inbox_search_empty_text)
|
||||
}
|
||||
|
||||
is Archived -> {
|
||||
@@ -302,78 +302,78 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
true -> getString(R.string.main_title_selected, state.page.selected)
|
||||
false -> getString(R.string.title_archived)
|
||||
}
|
||||
if (recyclerView.adapter !== conversationsAdapter)
|
||||
recyclerView.adapter = conversationsAdapter
|
||||
if (binding.recyclerView.adapter !== conversationsAdapter)
|
||||
binding.recyclerView.adapter = conversationsAdapter
|
||||
conversationsAdapter.updateData(state.page.data)
|
||||
itemTouchHelper.attachToRecyclerView(null)
|
||||
empty.setText(R.string.archived_empty_text)
|
||||
binding.empty.setText(R.string.archived_empty_text)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
inbox.isActivated = state.page is Inbox
|
||||
archived.isActivated = state.page is Archived
|
||||
binding.drawer.inbox.isActivated = state.page is Inbox
|
||||
binding.drawer.archived.isActivated = state.page is Archived
|
||||
|
||||
if (drawerLayout.isDrawerOpen(GravityCompat.START) && !state.drawerOpen)
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
else if (!drawerLayout.isDrawerVisible(GravityCompat.START) && state.drawerOpen)
|
||||
drawerLayout.openDrawer(GravityCompat.START)
|
||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START) && !state.drawerOpen)
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
else if (!binding.drawerLayout.isDrawerVisible(GravityCompat.START) && state.drawerOpen)
|
||||
binding.drawerLayout.openDrawer(GravityCompat.START)
|
||||
|
||||
when (state.syncing) {
|
||||
is SyncRepository.SyncProgress.Idle -> {
|
||||
syncing.isVisible = false
|
||||
snackbar.isVisible = (!state.defaultSms ||
|
||||
syncingBinding.root.isVisible = false
|
||||
snackbarBinding.root.isVisible = (!state.defaultSms ||
|
||||
!state.smsPermission ||
|
||||
!state.contactPermission ||
|
||||
!state.notificationPermission)
|
||||
}
|
||||
|
||||
is SyncRepository.SyncProgress.Running -> {
|
||||
syncing.isVisible = true
|
||||
syncingProgress.max = state.syncing.max
|
||||
syncingBinding.root.isVisible = true
|
||||
syncingBinding.syncingProgress.max = state.syncing.max
|
||||
progressAnimator.apply {
|
||||
setIntValues(syncingProgress.progress, state.syncing.progress)
|
||||
setIntValues(syncingBinding.syncingProgress.progress, state.syncing.progress)
|
||||
}.start()
|
||||
syncingProgress.isIndeterminate = state.syncing.indeterminate
|
||||
snackbar.isVisible = false
|
||||
syncingBinding.syncingProgress.isIndeterminate = state.syncing.indeterminate
|
||||
snackbarBinding.root.isVisible = false
|
||||
}
|
||||
|
||||
is SyncRepository.SyncProgress.ParsingEmojis -> {
|
||||
syncing.isVisible = true
|
||||
syncingLabel.setText(getString(R.string.main_sync_emojis))
|
||||
syncingProgress.max = state.syncing.max
|
||||
syncingBinding.root.isVisible = true
|
||||
syncingBinding.syncingLabel.setText(getString(R.string.main_sync_emojis))
|
||||
syncingBinding.syncingProgress.max = state.syncing.max
|
||||
progressAnimator.apply {
|
||||
setIntValues(syncingProgress.progress, state.syncing.progress)
|
||||
setIntValues(syncingBinding.syncingProgress.progress, state.syncing.progress)
|
||||
}.start()
|
||||
syncingProgress.isIndeterminate = state.syncing.indeterminate
|
||||
snackbar.isVisible = false
|
||||
syncingBinding.syncingProgress.isIndeterminate = state.syncing.indeterminate
|
||||
snackbarBinding.root.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
!state.defaultSms -> {
|
||||
snackbarTitle?.setText(R.string.main_default_sms_title)
|
||||
snackbarMessage?.setText(R.string.main_default_sms_message)
|
||||
snackbarButton?.setText(R.string.main_default_sms_change)
|
||||
snackbarBinding.snackbarTitle.setText(R.string.main_default_sms_title)
|
||||
snackbarBinding.snackbarMessage.setText(R.string.main_default_sms_message)
|
||||
snackbarBinding.snackbarButton.setText(R.string.main_default_sms_change)
|
||||
}
|
||||
|
||||
!state.smsPermission -> {
|
||||
snackbarTitle?.setText(R.string.main_permission_required)
|
||||
snackbarMessage?.setText(R.string.main_permission_sms)
|
||||
snackbarButton?.setText(R.string.main_permission_allow)
|
||||
snackbarBinding.snackbarTitle.setText(R.string.main_permission_required)
|
||||
snackbarBinding.snackbarMessage.setText(R.string.main_permission_sms)
|
||||
snackbarBinding.snackbarButton.setText(R.string.main_permission_allow)
|
||||
}
|
||||
|
||||
!state.contactPermission -> {
|
||||
snackbarTitle?.setText(R.string.main_permission_required)
|
||||
snackbarMessage?.setText(R.string.main_permission_contacts)
|
||||
snackbarButton?.setText(R.string.main_permission_allow)
|
||||
snackbarBinding.snackbarTitle.setText(R.string.main_permission_required)
|
||||
snackbarBinding.snackbarMessage.setText(R.string.main_permission_contacts)
|
||||
snackbarBinding.snackbarButton.setText(R.string.main_permission_allow)
|
||||
}
|
||||
|
||||
!state.notificationPermission -> {
|
||||
snackbarTitle?.setText(R.string.main_permission_required)
|
||||
snackbarMessage?.setText(R.string.main_permission_notifications)
|
||||
snackbarButton?.setText(R.string.main_permission_allow)
|
||||
snackbarBinding.snackbarTitle.setText(R.string.main_permission_required)
|
||||
snackbarBinding.snackbarMessage.setText(R.string.main_permission_notifications)
|
||||
snackbarBinding.snackbarButton.setText(R.string.main_permission_allow)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
|
||||
override fun showBackButton(show: Boolean) =
|
||||
toggle.let {
|
||||
it.onDrawerSlide(drawer, if (show) 1f else 0f)
|
||||
it.onDrawerSlide(binding.drawer.root, if (show) 1f else 0f)
|
||||
it.drawerArrowDrawable.color = when (show) {
|
||||
true -> resolveThemeColor(android.R.attr.textColorSecondary)
|
||||
false -> resolveThemeColor(android.R.attr.textColorPrimary)
|
||||
@@ -414,14 +414,14 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
|
||||
override fun clearSearch() {
|
||||
dismissKeyboard()
|
||||
toolbarSearch.text = null
|
||||
binding.toolbarSearch.text = null
|
||||
}
|
||||
|
||||
override fun clearSelection() = conversationsAdapter.clearSelection()
|
||||
|
||||
override fun toggleSelectAll() = conversationsAdapter.toggleSelectAll()
|
||||
|
||||
override fun themeChanged() = recyclerView.scrapViews()
|
||||
override fun themeChanged() = binding.recyclerView.scrapViews()
|
||||
|
||||
override fun showBlockingDialog(conversations: List<Long>, block: Boolean) {
|
||||
blockingDialog.show(this, conversations, block)
|
||||
@@ -456,7 +456,7 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
|
||||
override fun showArchivedSnackbar(countConversationsArchived: Int, isArchiving: Boolean) =
|
||||
Snackbar.make(
|
||||
drawerLayout,
|
||||
binding.drawerLayout,
|
||||
if (isArchiving) {
|
||||
resources.getQuantityString(R.plurals.toast_archived, countConversationsArchived, countConversationsArchived)
|
||||
} else {
|
||||
@@ -484,9 +484,9 @@ class MainActivity : QkThemedActivity(), MainView {
|
||||
override fun drawerToggled(opened: Boolean) {
|
||||
if (opened) {
|
||||
dismissKeyboard()
|
||||
if (!inbox.isInTouchMode)
|
||||
inbox.requestFocus()
|
||||
if (!binding.drawer.inbox.isInTouchMode)
|
||||
binding.drawer.inbox.requestFocus()
|
||||
} else
|
||||
toolbarSearch.requestFocus()
|
||||
binding.toolbarSearch.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
+14
-24
@@ -11,7 +11,6 @@ import com.jakewharton.rxbinding2.view.clicks
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.util.extensions.animateLayoutChanges
|
||||
import dev.octoshrimpy.quik.common.widget.QkSwitch
|
||||
import dev.octoshrimpy.quik.databinding.MessageUtilsControllerBinding
|
||||
import dev.octoshrimpy.quik.feature.settings.autodelete.AutoDeleteDialog
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
@@ -26,32 +25,30 @@ import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class MessageUtilsController : QkController<MessageUtilsView, MessageUtilsState, MessageUtilsPresenter>(), MessageUtilsView {
|
||||
class MessageUtilsController : QkController<MessageUtilsControllerBinding, MessageUtilsView, MessageUtilsState, MessageUtilsPresenter>(), MessageUtilsView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): MessageUtilsControllerBinding =
|
||||
MessageUtilsControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject lateinit var context: Context
|
||||
@Inject override lateinit var presenter: MessageUtilsPresenter
|
||||
private val autoDeleteDialog: AutoDeleteDialog by lazy {
|
||||
AutoDeleteDialog(activity!!, autoDeleteSubject::onNext)
|
||||
}
|
||||
private val autoDeleteSubject: Subject<Int> = PublishSubject.create()
|
||||
private var binding: MessageUtilsControllerBinding? = null
|
||||
override val autoDeduplicateClickIntent by lazy { binding!!.autoDeduplicate.clicks() }
|
||||
override val deduplicateClickIntent by lazy { binding!!.deduplicateMessages.clicks() }
|
||||
override val autoDeleteClickIntent by lazy { binding!!.autoDelete.clicks() }
|
||||
override val autoDeduplicateClickIntent by lazy { binding.autoDeduplicate.clicks() }
|
||||
override val deduplicateClickIntent by lazy { binding.deduplicateMessages.clicks() }
|
||||
override val autoDeleteClickIntent by lazy { binding.autoDelete.clicks() }
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
binding = MessageUtilsControllerBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
(binding?.root as? ViewGroup)?.postDelayed({
|
||||
binding?.parent?.animateLayoutChanges = true
|
||||
binding.root.postDelayed({
|
||||
binding.parent.animateLayoutChanges = true
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@@ -62,17 +59,10 @@ class MessageUtilsController : QkController<MessageUtilsView, MessageUtilsState,
|
||||
presenter.bindIntents(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
binding = null
|
||||
}
|
||||
|
||||
override fun render(state: MessageUtilsState) {
|
||||
binding?.autoDeduplicate
|
||||
?.findViewById<QkSwitch>(R.id.checkbox)
|
||||
?.isChecked = state.autoDeduplicateMessages
|
||||
binding.autoDeduplicate.checkbox?.isChecked = state.autoDeduplicateMessages
|
||||
|
||||
val deduplicationProgress = binding!!.deduplicationProgress
|
||||
val deduplicationProgress = binding.deduplicationProgress
|
||||
val progressAnimator = ObjectAnimator.ofInt(deduplicationProgress, "progress", 0, 0)
|
||||
|
||||
when (state.deduplicationProgress) {
|
||||
@@ -114,8 +104,8 @@ class MessageUtilsController : QkController<MessageUtilsView, MessageUtilsState,
|
||||
}
|
||||
|
||||
override fun handleDeduplicationResult(resIdString: Int) {
|
||||
binding?.deduplicationProgressText?.isVisible = true
|
||||
binding?.deduplicationProgressText?.setText(resIdString)
|
||||
binding.deduplicationProgressText.isVisible = true
|
||||
binding.deduplicationProgressText.setText(resIdString)
|
||||
}
|
||||
|
||||
override fun autoDeleteChanged(): Observable<Int> = autoDeleteSubject
|
||||
|
||||
+34
-32
@@ -37,15 +37,16 @@ import dev.octoshrimpy.quik.common.base.QkThemedActivity
|
||||
import dev.octoshrimpy.quik.common.util.extensions.animateLayoutChanges
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.databinding.NotificationPrefsActivityBinding
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.notification_prefs_activity.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationPrefsActivity : QkThemedActivity(), NotificationPrefsView {
|
||||
|
||||
private lateinit var binding: NotificationPrefsActivityBinding
|
||||
|
||||
@Inject lateinit var previewModeDialog: QkDialog
|
||||
@Inject lateinit var actionsDialog: QkDialog
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
@@ -62,27 +63,28 @@ class NotificationPrefsActivity : QkThemedActivity(), NotificationPrefsView {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.notification_prefs_activity)
|
||||
binding = NotificationPrefsActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setTitle(R.string.title_notification_prefs)
|
||||
showBackButton(true)
|
||||
viewModel.bindView(this)
|
||||
|
||||
preferences.postDelayed({ preferences?.animateLayoutChanges = true }, 100)
|
||||
binding.preferences.postDelayed({ binding.preferences.animateLayoutChanges = true }, 100)
|
||||
|
||||
val hasOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
|
||||
notificationsO.setVisible(hasOreo)
|
||||
notifications.setVisible(!hasOreo)
|
||||
vibration.setVisible(!hasOreo)
|
||||
ringtone.setVisible(!hasOreo)
|
||||
binding.notificationsO.setVisible(hasOreo)
|
||||
binding.notifications.setVisible(!hasOreo)
|
||||
binding.vibration.setVisible(!hasOreo)
|
||||
binding.ringtone.setVisible(!hasOreo)
|
||||
|
||||
previewModeDialog.setTitle(R.string.settings_notification_previews_title)
|
||||
previewModeDialog.adapter.setData(R.array.notification_preview_options)
|
||||
actionsDialog.adapter.setData(R.array.notification_actions)
|
||||
|
||||
// Listen to clicks for all of the preferences
|
||||
(0 until preferences.childCount)
|
||||
.map { index -> preferences.getChildAt(index) }
|
||||
(0 until binding.preferences.childCount)
|
||||
.map { index -> binding.preferences.getChildAt(index) }
|
||||
.mapNotNull { view -> view as? PreferenceView }
|
||||
.map { preference -> preference.clicks().map { preference } }
|
||||
.let { Observable.merge(it) }
|
||||
@@ -95,31 +97,31 @@ class NotificationPrefsActivity : QkThemedActivity(), NotificationPrefsView {
|
||||
title = state.conversationTitle
|
||||
}
|
||||
|
||||
notifications.checkbox.isChecked = state.notificationsEnabled
|
||||
previews.summary = state.previewSummary
|
||||
binding.notifications.checkbox?.isChecked = state.notificationsEnabled
|
||||
binding.previews.summary = state.previewSummary
|
||||
previewModeDialog.adapter.selectedItem = state.previewId
|
||||
wake.checkbox.isChecked = state.wakeEnabled
|
||||
silentNotContact.checkbox.isChecked = state.silentNotContact
|
||||
silentNotContact.isVisible = state.threadId == 0L
|
||||
vibration.checkbox.isChecked = state.vibrationEnabled
|
||||
ringtone.summary = state.ringtoneName
|
||||
binding.wake.checkbox?.isChecked = state.wakeEnabled
|
||||
binding.silentNotContact.checkbox?.isChecked = state.silentNotContact
|
||||
binding.silentNotContact.isVisible = state.threadId == 0L
|
||||
binding.vibration.checkbox?.isChecked = state.vibrationEnabled
|
||||
binding.ringtone.summary = state.ringtoneName
|
||||
|
||||
actionsDivider.isVisible = state.threadId == 0L
|
||||
actionsTitle.isVisible = state.threadId == 0L
|
||||
action1.isVisible = state.threadId == 0L
|
||||
action1.summary = state.action1Summary
|
||||
action2.isVisible = state.threadId == 0L
|
||||
action2.summary = state.action2Summary
|
||||
action3.isVisible = state.threadId == 0L
|
||||
action3.summary = state.action3Summary
|
||||
binding.actionsDivider.isVisible = state.threadId == 0L
|
||||
binding.actionsTitle.isVisible = state.threadId == 0L
|
||||
binding.action1.isVisible = state.threadId == 0L
|
||||
binding.action1.summary = state.action1Summary
|
||||
binding.action2.isVisible = state.threadId == 0L
|
||||
binding.action2.summary = state.action2Summary
|
||||
binding.action3.isVisible = state.threadId == 0L
|
||||
binding.action3.summary = state.action3Summary
|
||||
|
||||
qkreplyDivider.isVisible = state.threadId == 0L
|
||||
qkreplyTitle.isVisible = state.threadId == 0L
|
||||
qkreply.checkbox.isChecked = state.qkReplyEnabled
|
||||
qkreply.isVisible = state.threadId == 0L
|
||||
qkreplyTapDismiss.isVisible = state.threadId == 0L
|
||||
qkreplyTapDismiss.isEnabled = state.qkReplyEnabled
|
||||
qkreplyTapDismiss.checkbox.isChecked = state.qkReplyTapDismiss
|
||||
binding.qkreplyDivider.isVisible = state.threadId == 0L
|
||||
binding.qkreplyTitle.isVisible = state.threadId == 0L
|
||||
binding.qkreply.checkbox?.isChecked = state.qkReplyEnabled
|
||||
binding.qkreply.isVisible = state.threadId == 0L
|
||||
binding.qkreplyTapDismiss.isVisible = state.threadId == 0L
|
||||
binding.qkreplyTapDismiss.isEnabled = state.qkReplyEnabled
|
||||
binding.qkreplyTapDismiss.checkbox?.isChecked = state.qkReplyTapDismiss
|
||||
}
|
||||
|
||||
override fun showPreviewModeDialog() = previewModeDialog.show(this)
|
||||
|
||||
@@ -83,7 +83,7 @@ class PlusActivity : QkThemedActivity(), PlusView {
|
||||
// Make the list titles bold
|
||||
binding.linearLayout.children
|
||||
.mapNotNull { it as? PreferenceView }
|
||||
.map { it.titleTextView }
|
||||
.map { it.titleView }
|
||||
.forEach { it.setTypeface(it.typeface, Typeface.BOLD) }
|
||||
|
||||
val textPrimary = resolveThemeColor(android.R.attr.textColorPrimary)
|
||||
|
||||
@@ -45,22 +45,23 @@ import dev.octoshrimpy.quik.common.util.extensions.autoScrollToStart
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.util.extensions.showKeyboard
|
||||
import dev.octoshrimpy.quik.common.widget.QkEditText
|
||||
import dev.octoshrimpy.quik.databinding.QkreplyActivityBinding
|
||||
import dev.octoshrimpy.quik.feature.compose.MessagesAdapter
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.compose_activity.message
|
||||
import kotlinx.android.synthetic.main.qkreply_activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class QkReplyActivity : QkThemedActivity(), QkReplyView {
|
||||
|
||||
private lateinit var binding: QkreplyActivityBinding
|
||||
|
||||
@Inject lateinit var adapter: MessagesAdapter
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
override val menuItemIntent: Subject<Int> = PublishSubject.create()
|
||||
override val textChangedIntent by lazy { message.textChanges() }
|
||||
override val changeSimIntent by lazy { sim.clicks() }
|
||||
override val sendIntent by lazy { send.clicks() }
|
||||
override val textChangedIntent by lazy { binding.message.textChanges() }
|
||||
override val changeSimIntent by lazy { binding.sim.clicks() }
|
||||
override val sendIntent by lazy { binding.send.clicks() }
|
||||
|
||||
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory)[QkReplyViewModel::class.java] }
|
||||
|
||||
@@ -90,21 +91,22 @@ class QkReplyActivity : QkThemedActivity(), QkReplyView {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setFinishOnTouchOutside(prefs.qkreplyTapDismiss.get())
|
||||
setContentView(R.layout.qkreply_activity)
|
||||
binding = QkreplyActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window.setBackgroundDrawable(null)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
viewModel.bindView(this)
|
||||
|
||||
toolbar.clipToOutline = true
|
||||
binding.toolbar.clipToOutline = true
|
||||
|
||||
messages.adapter = adapter
|
||||
messages.adapter?.autoScrollToStart(messages)
|
||||
messages.adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() = messages.scrollToPosition(adapter.itemCount - 1)
|
||||
binding.messages.adapter = adapter
|
||||
binding.messages.adapter?.autoScrollToStart(binding.messages)
|
||||
binding.messages.adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() = binding.messages.scrollToPosition(adapter.itemCount - 1)
|
||||
})
|
||||
|
||||
message.setOnTouchListener(object : OnTouchListener {
|
||||
binding.message.setOnTouchListener(object : OnTouchListener {
|
||||
private val gestureDetector =
|
||||
GestureDetector(this@QkReplyActivity, object : SimpleOnGestureListener() {
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
@@ -119,7 +121,7 @@ class QkReplyActivity : QkThemedActivity(), QkReplyView {
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
message.showKeyboard()
|
||||
binding.message.showKeyboard()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -143,24 +145,24 @@ class QkReplyActivity : QkThemedActivity(), QkReplyView {
|
||||
|
||||
title = state.title
|
||||
|
||||
toolbar.menu.findItem(R.id.expand)?.isVisible = !state.expanded
|
||||
toolbar.menu.findItem(R.id.collapse)?.isVisible = state.expanded
|
||||
binding.toolbar.menu.findItem(R.id.expand)?.isVisible = !state.expanded
|
||||
binding.toolbar.menu.findItem(R.id.collapse)?.isVisible = state.expanded
|
||||
|
||||
adapter.data = state.data
|
||||
|
||||
counter.text = state.remaining
|
||||
counter.setVisible(counter.text.isNotBlank())
|
||||
binding.counter.text = state.remaining
|
||||
binding.counter.setVisible(binding.counter.text.isNotBlank())
|
||||
|
||||
sim.setVisible(state.subscription != null)
|
||||
sim.contentDescription = getString(R.string.compose_sim_cd, state.subscription?.displayName)
|
||||
simIndex.text = "${state.subscription?.simSlotIndex?.plus(1)}"
|
||||
binding.sim.setVisible(state.subscription != null)
|
||||
binding.sim.contentDescription = getString(R.string.compose_sim_cd, state.subscription?.displayName)
|
||||
binding.simIndex.text = "${state.subscription?.simSlotIndex?.plus(1)}"
|
||||
|
||||
send.isEnabled = state.canSend
|
||||
send.imageAlpha = if (state.canSend) 255 else 128
|
||||
binding.send.isEnabled = state.canSend
|
||||
binding.send.imageAlpha = if (state.canSend) 255 else 128
|
||||
}
|
||||
|
||||
override fun setDraft(draft: String) {
|
||||
message.setText(draft)
|
||||
binding.message.setText(draft)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
|
||||
@@ -31,20 +31,21 @@ import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkThemedActivity
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.ScheduledActivityBinding
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.main_activity.toolbar
|
||||
import kotlinx.android.synthetic.main.scheduled_activity.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class ScheduledActivity : QkThemedActivity(), ScheduledView {
|
||||
|
||||
private lateinit var binding: ScheduledActivityBinding
|
||||
|
||||
@Inject lateinit var scheduledMessageAdapter: ScheduledMessageAdapter
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
override val composeIntent by lazy { compose.clicks() }
|
||||
override val upgradeIntent by lazy { upgrade.clicks() }
|
||||
override val composeIntent by lazy { binding.compose.clicks() }
|
||||
override val upgradeIntent by lazy { binding.upgrade.clicks() }
|
||||
override val messagesSelectedIntent by lazy { scheduledMessageAdapter.selectionChanges }
|
||||
override val optionsItemIntent: Subject<Int> = PublishSubject.create()
|
||||
override val deleteScheduledMessages: Subject<List<Long>> = PublishSubject.create()
|
||||
@@ -59,22 +60,23 @@ class ScheduledActivity : QkThemedActivity(), ScheduledView {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.scheduled_activity)
|
||||
binding = ScheduledActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setTitle(R.string.scheduled_title)
|
||||
showBackButton(true)
|
||||
viewModel.bindView(this)
|
||||
|
||||
scheduledMessageAdapter.emptyView = empty
|
||||
messages.adapter = scheduledMessageAdapter
|
||||
scheduledMessageAdapter.emptyView = binding.empty
|
||||
binding.messages.adapter = scheduledMessageAdapter
|
||||
|
||||
colors.theme().let { theme ->
|
||||
sampleMessage.setBackgroundTint(theme.theme)
|
||||
sampleMessage.setTextColor(theme.textPrimary)
|
||||
compose.setTint(theme.textPrimary)
|
||||
compose.setBackgroundTint(theme.theme)
|
||||
upgrade.setBackgroundTint(theme.theme)
|
||||
upgradeIcon.setTint(theme.textPrimary)
|
||||
upgradeLabel.setTextColor(theme.textPrimary)
|
||||
binding.sampleMessage.setBackgroundTint(theme.theme)
|
||||
binding.sampleMessage.setTextColor(theme.textPrimary)
|
||||
binding.compose.setTint(theme.textPrimary)
|
||||
binding.compose.setBackgroundTint(theme.theme)
|
||||
binding.upgrade.setBackgroundTint(theme.theme)
|
||||
binding.upgradeIcon.setTint(theme.textPrimary)
|
||||
binding.upgradeLabel.setTextColor(theme.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,20 +90,20 @@ class ScheduledActivity : QkThemedActivity(), ScheduledView {
|
||||
})
|
||||
|
||||
// show/hide menu items
|
||||
toolbar.menu.findItem(R.id.select_all)?.isVisible =
|
||||
toolbar?.menu?.findItem(R.id.select_all)?.isVisible =
|
||||
((scheduledMessageAdapter.itemCount > 1) && (state.selectedMessages != 0))
|
||||
toolbar.menu.findItem(R.id.delete)?.isVisible =
|
||||
toolbar?.menu?.findItem(R.id.delete)?.isVisible =
|
||||
((scheduledMessageAdapter.itemCount != 0) && (state.selectedMessages != 0))
|
||||
toolbar.menu.findItem(R.id.copy)?.isVisible =
|
||||
toolbar?.menu?.findItem(R.id.copy)?.isVisible =
|
||||
((scheduledMessageAdapter.itemCount != 0) && (state.selectedMessages != 0))
|
||||
toolbar.menu.findItem(R.id.send_now)?.isVisible =
|
||||
toolbar?.menu?.findItem(R.id.send_now)?.isVisible =
|
||||
((scheduledMessageAdapter.itemCount != 0) && (state.selectedMessages != 0))
|
||||
toolbar.menu.findItem(R.id.edit_message)?.isVisible =
|
||||
toolbar?.menu?.findItem(R.id.edit_message)?.isVisible =
|
||||
((scheduledMessageAdapter.itemCount != 0) && (state.selectedMessages == 1))
|
||||
|
||||
// show compose button
|
||||
compose.isVisible = state.upgraded && (state.conversationId == null)
|
||||
upgrade.isVisible = !state.upgraded
|
||||
binding.compose.isVisible = state.upgraded && (state.conversationId == null)
|
||||
binding.upgrade.isVisible = !state.upgraded
|
||||
}
|
||||
|
||||
override fun onBackPressed() = backPressedIntent.onNext(Unit)
|
||||
|
||||
@@ -23,20 +23,21 @@ import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import dagger.android.AndroidInjection
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkThemedActivity
|
||||
import kotlinx.android.synthetic.main.container_activity.*
|
||||
import dev.octoshrimpy.quik.databinding.ContainerActivityBinding
|
||||
|
||||
class SettingsActivity : QkThemedActivity() {
|
||||
|
||||
private lateinit var binding: ContainerActivityBinding
|
||||
private lateinit var router: Router
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.container_activity)
|
||||
binding = ContainerActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
router = Conductor.attachRouter(this, container, savedInstanceState)
|
||||
router = Conductor.attachRouter(this, binding.container, savedInstanceState)
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(SettingsController()))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ import android.app.TimePickerDialog
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.text.format.DateFormat
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
@@ -43,6 +45,7 @@ import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.common.widget.TextInputDialog
|
||||
import dev.octoshrimpy.quik.databinding.SettingsControllerBinding
|
||||
import dev.octoshrimpy.quik.feature.settings.about.AboutController
|
||||
import dev.octoshrimpy.quik.feature.settings.swipe.SwipeActionsController
|
||||
import dev.octoshrimpy.quik.feature.themepicker.ThemePickerController
|
||||
@@ -52,13 +55,12 @@ import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.settings_controller.*
|
||||
import kotlinx.android.synthetic.main.settings_controller.view.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
import kotlinx.android.synthetic.main.settings_theme_widget.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class SettingsController : QkController<SettingsView, SettingsState, SettingsPresenter>(), SettingsView {
|
||||
class SettingsController : QkController<SettingsControllerBinding, SettingsView, SettingsState, SettingsPresenter>(), SettingsView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): SettingsControllerBinding =
|
||||
SettingsControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var colors: Colors
|
||||
@@ -79,12 +81,11 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
private val endTimeSelectedSubject: Subject<Pair<Int, Int>> = PublishSubject.create()
|
||||
private val signatureSubject: Subject<String> = PublishSubject.create()
|
||||
|
||||
private val progressAnimator by lazy { ObjectAnimator.ofInt(syncingProgress, "progress", 0, 0) }
|
||||
private val progressAnimator by lazy { ObjectAnimator.ofInt(binding.syncingProgress, "progress", 0, 0) }
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.settings_controller
|
||||
|
||||
colors.themeObservable()
|
||||
.autoDisposable(scope())
|
||||
@@ -92,7 +93,7 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
preferences.postDelayed({ preferences?.animateLayoutChanges = true }, 100)
|
||||
binding.preferences.postDelayed({ binding.preferences.animateLayoutChanges = true }, 100)
|
||||
|
||||
when (Build.VERSION.SDK_INT >= 29) {
|
||||
true -> nightModeDialog.adapter.setData(R.array.night_modes)
|
||||
@@ -105,7 +106,7 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
mmsSizeDialog.adapter.setData(R.array.mms_sizes, R.array.mms_sizes_ids)
|
||||
messageLinkHandlingDialog.adapter.setData(R.array.messageLinkHandlings, R.array.messageLinkHandling_ids)
|
||||
|
||||
about.summary = context.getString(R.string.settings_version, BuildConfig.VERSION_NAME)
|
||||
binding.about.summary = context.getString(R.string.settings_version, BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -115,13 +116,13 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
showBackButton(true)
|
||||
}
|
||||
|
||||
override fun preferenceClicks(): Observable<PreferenceView> = (0 until preferences.childCount)
|
||||
.map { index -> preferences.getChildAt(index) }
|
||||
override fun preferenceClicks(): Observable<PreferenceView> = (0 until binding.preferences.childCount)
|
||||
.map { index -> binding.preferences.getChildAt(index) }
|
||||
.mapNotNull { view -> view as? PreferenceView }
|
||||
.map { preference -> preference.clicks().map { preference } }
|
||||
.let { preferences -> Observable.merge(preferences) }
|
||||
|
||||
override fun aboutLongClicks(): Observable<*> = about.longClicks()
|
||||
override fun aboutLongClicks(): Observable<*> = binding.about.longClicks()
|
||||
|
||||
override fun viewQksmsPlusClicks(): Observable<*> = viewQksmsPlusSubject
|
||||
|
||||
@@ -142,73 +143,73 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
override fun messageLinkHandlingSelected(): Observable<Int> = messageLinkHandlingDialog.adapter.menuItemClicks
|
||||
|
||||
override fun render(state: SettingsState) {
|
||||
themePreview.setBackgroundTint(state.theme)
|
||||
night.summary = state.nightModeSummary
|
||||
binding.theme.findViewById<View>(R.id.themePreview)?.setBackgroundTint(state.theme)
|
||||
binding.night.summary = state.nightModeSummary
|
||||
nightModeDialog.adapter.selectedItem = state.nightModeId
|
||||
nightStart.setVisible(state.nightModeId == Preferences.NIGHT_MODE_AUTO)
|
||||
nightStart.summary = state.nightStart
|
||||
nightEnd.setVisible(state.nightModeId == Preferences.NIGHT_MODE_AUTO)
|
||||
nightEnd.summary = state.nightEnd
|
||||
binding.nightStart.setVisible(state.nightModeId == Preferences.NIGHT_MODE_AUTO)
|
||||
binding.nightStart.summary = state.nightStart
|
||||
binding.nightEnd.setVisible(state.nightModeId == Preferences.NIGHT_MODE_AUTO)
|
||||
binding.nightEnd.summary = state.nightEnd
|
||||
|
||||
black.setVisible(state.nightModeId != Preferences.NIGHT_MODE_OFF)
|
||||
black.checkbox.isChecked = state.black
|
||||
binding.black.setVisible(state.nightModeId != Preferences.NIGHT_MODE_OFF)
|
||||
binding.black.checkbox?.isChecked = state.black
|
||||
|
||||
autoEmoji.checkbox.isChecked = state.autoEmojiEnabled
|
||||
binding.autoEmoji.checkbox?.isChecked = state.autoEmojiEnabled
|
||||
|
||||
delayed.summary = state.sendDelaySummary
|
||||
binding.delayed.summary = state.sendDelaySummary
|
||||
sendDelayDialog.adapter.selectedItem = state.sendDelayId
|
||||
|
||||
delivery.checkbox.isChecked = state.deliveryEnabled
|
||||
binding.delivery.checkbox?.isChecked = state.deliveryEnabled
|
||||
|
||||
unreadAtTop.checkbox.isChecked = state.unreadAtTopEnabled
|
||||
binding.unreadAtTop.checkbox?.isChecked = state.unreadAtTopEnabled
|
||||
|
||||
signature.summary = state.signature.takeIf { it.isNotBlank() }
|
||||
binding.signature.summary = state.signature.takeIf { it.isNotBlank() }
|
||||
?: context.getString(R.string.settings_signature_summary)
|
||||
|
||||
textSize.summary = state.textSizeSummary
|
||||
binding.textSize.summary = state.textSizeSummary
|
||||
textSizeDialog.adapter.selectedItem = state.textSizeId
|
||||
|
||||
autoColor.checkbox.isChecked = state.autoColor
|
||||
binding.autoColor.checkbox?.isChecked = state.autoColor
|
||||
|
||||
systemFont.checkbox.isChecked = state.systemFontEnabled
|
||||
binding.systemFont.checkbox?.isChecked = state.systemFontEnabled
|
||||
|
||||
showStt.checkbox.isChecked = state.showStt
|
||||
binding.showStt.checkbox?.isChecked = state.showStt
|
||||
|
||||
unicode.checkbox.isChecked = state.stripUnicodeEnabled
|
||||
mobileOnly.checkbox.isChecked = state.mobileOnly
|
||||
binding.unicode.checkbox?.isChecked = state.stripUnicodeEnabled
|
||||
binding.mobileOnly.checkbox?.isChecked = state.mobileOnly
|
||||
|
||||
longAsMms.checkbox.isChecked = state.longAsMms
|
||||
binding.longAsMms.checkbox?.isChecked = state.longAsMms
|
||||
|
||||
mmsSize.summary = state.maxMmsSizeSummary
|
||||
binding.mmsSize.summary = state.maxMmsSizeSummary
|
||||
mmsSizeDialog.adapter.selectedItem = state.maxMmsSizeId
|
||||
|
||||
messsageLinkHandling.summary = state.messageLinkHandlingSummary
|
||||
binding.messsageLinkHandling.summary = state.messageLinkHandlingSummary
|
||||
messageLinkHandlingDialog.adapter.selectedItem = state.messageLinkHandlingId
|
||||
|
||||
disableScreenshots.checkbox.isChecked = state.disableScreenshotsEnabled
|
||||
binding.disableScreenshots.checkbox?.isChecked = state.disableScreenshotsEnabled
|
||||
|
||||
when (state.syncProgress) {
|
||||
is SyncRepository.SyncProgress.Idle -> syncingProgress.isVisible = false
|
||||
is SyncRepository.SyncProgress.Idle -> binding.syncingProgress.isVisible = false
|
||||
|
||||
is SyncRepository.SyncProgress.Running -> {
|
||||
syncingProgress.isVisible = true
|
||||
syncingProgress.max = state.syncProgress.max
|
||||
progressAnimator.apply { setIntValues(syncingProgress.progress, state.syncProgress.progress) }.start()
|
||||
syncingProgress.isIndeterminate = state.syncProgress.indeterminate
|
||||
binding.syncingProgress.isVisible = true
|
||||
binding.syncingProgress.max = state.syncProgress.max
|
||||
progressAnimator.apply { setIntValues(binding.syncingProgress.progress, state.syncProgress.progress) }.start()
|
||||
binding.syncingProgress.isIndeterminate = state.syncProgress.indeterminate
|
||||
}
|
||||
|
||||
is SyncRepository.SyncProgress.ParsingEmojis -> {
|
||||
syncingProgress.isVisible = true
|
||||
syncingProgress.max = state.syncProgress.max
|
||||
progressAnimator.apply { setIntValues(syncingProgress.progress, state.syncProgress.progress) }.start()
|
||||
syncingProgress.isIndeterminate = state.syncProgress.indeterminate
|
||||
binding.syncingProgress.isVisible = true
|
||||
binding.syncingProgress.max = state.syncProgress.max
|
||||
progressAnimator.apply { setIntValues(binding.syncingProgress.progress, state.syncProgress.progress) }.start()
|
||||
binding.syncingProgress.isIndeterminate = state.syncProgress.indeterminate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showQksmsPlusSnackbar() {
|
||||
view?.run {
|
||||
Snackbar.make(contentView, R.string.toast_qksms_plus, Snackbar.LENGTH_LONG).run {
|
||||
Snackbar.make(binding.contentView, R.string.toast_qksms_plus, Snackbar.LENGTH_LONG).run {
|
||||
setAction(R.string.button_more) { viewQksmsPlusSubject.onNext(Unit) }
|
||||
setActionTextColor(colors.theme().theme)
|
||||
show()
|
||||
@@ -259,4 +260,4 @@ class SettingsController : QkController<SettingsView, SettingsState, SettingsPre
|
||||
.popChangeHandler(QkChangeHandler()))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -18,28 +18,32 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.settings.about
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import dev.octoshrimpy.quik.BuildConfig
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.databinding.AboutControllerBinding
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import io.reactivex.Observable
|
||||
import kotlinx.android.synthetic.main.about_controller.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class AboutController : QkController<AboutView, Unit, AboutPresenter>(), AboutView {
|
||||
class AboutController : QkController<AboutControllerBinding, AboutView, Unit, AboutPresenter>(), AboutView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): AboutControllerBinding =
|
||||
AboutControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject override lateinit var presenter: AboutPresenter
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
layoutRes = R.layout.about_controller
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
version.summary = BuildConfig.VERSION_NAME
|
||||
binding.version.summary = BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -49,8 +53,8 @@ class AboutController : QkController<AboutView, Unit, AboutPresenter>(), AboutVi
|
||||
showBackButton(true)
|
||||
}
|
||||
|
||||
override fun preferenceClicks(): Observable<PreferenceView> = (0 until preferences.childCount)
|
||||
.map { index -> preferences.getChildAt(index) }
|
||||
override fun preferenceClicks(): Observable<PreferenceView> = (0 until binding.preferences.childCount)
|
||||
.map { index -> binding.preferences.getChildAt(index) }
|
||||
.mapNotNull { view -> view as? PreferenceView }
|
||||
.map { preference -> preference.clicks().map { preference } }
|
||||
.let { preferences -> Observable.merge(preferences) }
|
||||
|
||||
+21
-17
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.settings.swipe
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import com.uber.autodispose.android.lifecycle.scope
|
||||
@@ -30,14 +32,17 @@ import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.animateLayoutChanges
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.databinding.SwipeActionsControllerBinding
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.swipe_actions_controller.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class SwipeActionsController : QkController<SwipeActionsView, SwipeActionsState, SwipeActionsPresenter>(), SwipeActionsView {
|
||||
class SwipeActionsController : QkController<SwipeActionsControllerBinding, SwipeActionsView, SwipeActionsState, SwipeActionsPresenter>(), SwipeActionsView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): SwipeActionsControllerBinding =
|
||||
SwipeActionsControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject override lateinit var presenter: SwipeActionsPresenter
|
||||
@Inject lateinit var actionsDialog: QkDialog
|
||||
@@ -50,25 +55,24 @@ class SwipeActionsController : QkController<SwipeActionsView, SwipeActionsState,
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
layoutRes = R.layout.swipe_actions_controller
|
||||
|
||||
actionsDialog.adapter.setData(R.array.settings_swipe_actions)
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
colors.theme().let { theme ->
|
||||
rightIcon.setBackgroundTint(theme.theme)
|
||||
rightIcon.setTint(theme.textPrimary)
|
||||
leftIcon.setBackgroundTint(theme.theme)
|
||||
leftIcon.setTint(theme.textPrimary)
|
||||
binding.rightIcon.setBackgroundTint(theme.theme)
|
||||
binding.rightIcon.setTint(theme.textPrimary)
|
||||
binding.leftIcon.setBackgroundTint(theme.theme)
|
||||
binding.leftIcon.setTint(theme.textPrimary)
|
||||
}
|
||||
|
||||
right.postDelayed({ right?.animateLayoutChanges = true }, 100)
|
||||
left.postDelayed({ left?.animateLayoutChanges = true }, 100)
|
||||
binding.right.postDelayed({ binding.right.animateLayoutChanges = true }, 100)
|
||||
binding.left.postDelayed({ binding.left.animateLayoutChanges = true }, 100)
|
||||
|
||||
Observable.merge(
|
||||
right.clicks().map { SwipeActionsView.Action.RIGHT },
|
||||
left.clicks().map { SwipeActionsView.Action.LEFT })
|
||||
binding.right.clicks().map { SwipeActionsView.Action.RIGHT },
|
||||
binding.left.clicks().map { SwipeActionsView.Action.LEFT })
|
||||
.autoDisposable(scope())
|
||||
.subscribe(actionClicks)
|
||||
}
|
||||
@@ -90,13 +94,13 @@ class SwipeActionsController : QkController<SwipeActionsView, SwipeActionsState,
|
||||
}
|
||||
|
||||
override fun render(state: SwipeActionsState) {
|
||||
rightIcon.isVisible = state.rightIcon != 0
|
||||
rightIcon.setImageResource(state.rightIcon)
|
||||
rightLabel.text = state.rightLabel
|
||||
binding.rightIcon.isVisible = state.rightIcon != 0
|
||||
binding.rightIcon.setImageResource(state.rightIcon)
|
||||
binding.rightLabel.text = state.rightLabel
|
||||
|
||||
leftIcon.isVisible = state.leftIcon != 0
|
||||
leftIcon.setImageResource(state.leftIcon)
|
||||
leftLabel.text = state.leftLabel
|
||||
binding.leftIcon.isVisible = state.leftIcon != 0
|
||||
binding.leftIcon.setImageResource(state.leftIcon)
|
||||
binding.leftLabel.text = state.leftLabel
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,16 +22,15 @@ import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.within
|
||||
import dev.octoshrimpy.quik.databinding.HsvPickerViewBinding
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.hsv_picker_view.view.*
|
||||
|
||||
class HSVPickerView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
@@ -51,13 +50,15 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
updateHue()
|
||||
}
|
||||
|
||||
private val binding: HsvPickerViewBinding
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.hsv_picker_view, this)
|
||||
binding = HsvPickerViewBinding.inflate(LayoutInflater.from(context), this)
|
||||
|
||||
var swatchX = 0f
|
||||
var swatchY = 0f
|
||||
|
||||
saturation.setOnTouchListener { _, event ->
|
||||
binding.saturation.setOnTouchListener { _, event ->
|
||||
setupBounds()
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
@@ -68,8 +69,8 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// Calculate the new x/y position
|
||||
swatch.x = (event.rawX + swatchX + min).within(min, max)
|
||||
swatch.y = (event.rawY + swatchY + min).within(min, max)
|
||||
binding.swatch.x = (event.rawX + swatchX + min).within(min, max)
|
||||
binding.swatch.y = (event.rawY + swatchY + min).within(min, max)
|
||||
|
||||
updateSelectedColor()
|
||||
}
|
||||
@@ -85,7 +86,7 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
|
||||
var hueThumbX = 0f
|
||||
|
||||
hueGroup.setOnTouchListener { _, event ->
|
||||
binding.hueGroup.setOnTouchListener { _, event ->
|
||||
setupBounds()
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
@@ -96,8 +97,8 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val x = (event.rawX + hueThumbX + min).within(min, max)
|
||||
|
||||
hueThumb.x = x
|
||||
hue = (hueThumb.x - min) / (max - min) * 360
|
||||
binding.hueThumb.x = x
|
||||
hue = (binding.hueThumb.x - min) / (max - min) * 360
|
||||
|
||||
updateSelectedColor()
|
||||
}
|
||||
@@ -111,14 +112,14 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
true
|
||||
}
|
||||
|
||||
hueTrack.clipToOutline = true
|
||||
hueTrack.setImageDrawable(GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, hues))
|
||||
binding.hueTrack.clipToOutline = true
|
||||
binding.hueTrack.setImageDrawable(GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, hues))
|
||||
}
|
||||
|
||||
private fun setupBounds() {
|
||||
if (min == 0f || max == 0f) {
|
||||
min = saturation.x - swatch.width / 2
|
||||
max = min + saturation.width
|
||||
min = binding.saturation.x - binding.swatch.width / 2
|
||||
max = min + binding.saturation.width
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,10 +127,10 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
setupBounds()
|
||||
|
||||
val range = max - min
|
||||
val hsv = floatArrayOf(hue, (swatch.x - min) / range, 1 - (swatch.y - min) / range)
|
||||
val hsv = floatArrayOf(hue, (binding.swatch.x - min) / range, 1 - (binding.swatch.y - min) / range)
|
||||
val color = Color.HSVToColor(hsv)
|
||||
|
||||
swatch.setTint(color)
|
||||
binding.swatch.setTint(color)
|
||||
selectedColor.onNext(color)
|
||||
}
|
||||
|
||||
@@ -145,9 +146,9 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
setupBounds()
|
||||
val range = max - min
|
||||
|
||||
hueThumb.x = range * hsv[0] / 360 + min
|
||||
swatch.x = range * hsv[1] + min
|
||||
swatch.y = range * (1 - hsv[2]) + min
|
||||
binding.hueThumb.x = range * hsv[0] / 360 + min
|
||||
binding.swatch.x = range * hsv[1] + min
|
||||
binding.swatch.y = range * (1 - hsv[2]) + min
|
||||
|
||||
updateSelectedColor()
|
||||
}
|
||||
@@ -156,7 +157,7 @@ class HSVPickerView @JvmOverloads constructor(
|
||||
private fun updateHue() {
|
||||
val hsv = floatArrayOf(hue, 1f, 1f)
|
||||
val tint = Color.HSVToColor(hsv)
|
||||
saturation.setBackgroundTint(tint)
|
||||
binding.saturation.setBackgroundTint(tint)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,25 +25,23 @@ import android.view.ViewGroup
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.common.base.QkBindingViewHolder
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.dpToPx
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.databinding.ThemeListItemBinding
|
||||
import dev.octoshrimpy.quik.databinding.ThemePaletteListItemBinding
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.theme_list_item.view.*
|
||||
import kotlinx.android.synthetic.main.theme_palette_list_item.*
|
||||
import kotlinx.android.synthetic.main.theme_palette_list_item.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ThemeAdapter @Inject constructor(
|
||||
private val context: Context,
|
||||
private val colors: Colors
|
||||
) : QkAdapter<List<Int>, QkViewHolder>() {
|
||||
) : QkAdapter<List<Int>, QkBindingViewHolder<ThemePaletteListItemBinding>>() {
|
||||
|
||||
val colorSelected: Subject<Int> = PublishSubject.create()
|
||||
|
||||
@@ -61,16 +59,17 @@ class ThemeAdapter @Inject constructor(
|
||||
|
||||
private var iconTint = 0
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.theme_palette_list_item, parent, false)
|
||||
view.palette.flexWrap = FlexWrap.WRAP
|
||||
view.palette.flexDirection = FlexDirection.ROW
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkBindingViewHolder<ThemePaletteListItemBinding> {
|
||||
val binding = ThemePaletteListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
binding.palette.flexWrap = FlexWrap.WRAP
|
||||
binding.palette.flexDirection = FlexDirection.ROW
|
||||
|
||||
return QkViewHolder(view)
|
||||
return QkBindingViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: QkBindingViewHolder<ThemePaletteListItemBinding>, position: Int) {
|
||||
val palette = getItem(position)
|
||||
val binding = holder.binding
|
||||
|
||||
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||
val minPadding = (16 * 6).dpToPx(context)
|
||||
@@ -81,33 +80,34 @@ class ThemeAdapter @Inject constructor(
|
||||
}
|
||||
val swatchPadding = (screenWidth - size * 5) / 12
|
||||
|
||||
holder.palette.removeAllViews()
|
||||
holder.palette.setPadding(swatchPadding, swatchPadding, swatchPadding, swatchPadding)
|
||||
binding.palette.removeAllViews()
|
||||
binding.palette.setPadding(swatchPadding, swatchPadding, swatchPadding, swatchPadding)
|
||||
|
||||
(palette.subList(0, 5) + palette.subList(5, 10).reversed())
|
||||
.mapIndexed { index, color ->
|
||||
LayoutInflater.from(context).inflate(R.layout.theme_list_item, holder.palette, false).apply {
|
||||
val itemBinding = ThemeListItemBinding.inflate(LayoutInflater.from(context), binding.palette, false)
|
||||
|
||||
// Send clicks to the selected subject
|
||||
setOnClickListener { colorSelected.onNext(color) }
|
||||
// Send clicks to the selected subject
|
||||
itemBinding.root.setOnClickListener { colorSelected.onNext(color) }
|
||||
|
||||
// Apply the color to the view
|
||||
theme.setBackgroundTint(color)
|
||||
// Apply the color to the view
|
||||
itemBinding.theme.setBackgroundTint(color)
|
||||
|
||||
// Control the check visibility and tint
|
||||
check.setVisible(color == selectedColor)
|
||||
check.setTint(iconTint)
|
||||
// Control the check visibility and tint
|
||||
itemBinding.check.setVisible(color == selectedColor)
|
||||
itemBinding.check.setTint(iconTint)
|
||||
|
||||
// Update the size so that the spacing is perfectly even
|
||||
layoutParams = (layoutParams as FlexboxLayout.LayoutParams).apply {
|
||||
height = size
|
||||
width = size
|
||||
isWrapBefore = index % 5 == 0
|
||||
setMargins(swatchPadding, swatchPadding, swatchPadding, swatchPadding)
|
||||
}
|
||||
// Update the size so that the spacing is perfectly even
|
||||
itemBinding.root.layoutParams = (itemBinding.root.layoutParams as FlexboxLayout.LayoutParams).apply {
|
||||
height = size
|
||||
width = size
|
||||
isWrapBefore = index % 5 == 0
|
||||
setMargins(swatchPadding, swatchPadding, swatchPadding, swatchPadding)
|
||||
}
|
||||
|
||||
itemBinding.root
|
||||
}
|
||||
.forEach { theme -> holder.palette.addView(theme) }
|
||||
.forEach { theme -> binding.palette.addView(theme) }
|
||||
}
|
||||
|
||||
}
|
||||
+23
-21
@@ -19,7 +19,9 @@
|
||||
package dev.octoshrimpy.quik.feature.themepicker
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
@@ -29,18 +31,20 @@ import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.dpToPx
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setVisible
|
||||
import dev.octoshrimpy.quik.databinding.ThemePickerControllerBinding
|
||||
import dev.octoshrimpy.quik.feature.themepicker.injection.ThemePickerModule
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.theme_picker_controller.*
|
||||
import kotlinx.android.synthetic.main.theme_picker_hsv.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ThemePickerController(
|
||||
val recipientId: Long = 0L
|
||||
) : QkController<ThemePickerView, ThemePickerState, ThemePickerPresenter>(), ThemePickerView {
|
||||
) : QkController<ThemePickerControllerBinding, ThemePickerView, ThemePickerState, ThemePickerPresenter>(), ThemePickerView {
|
||||
|
||||
override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup): ThemePickerControllerBinding =
|
||||
ThemePickerControllerBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject override lateinit var presenter: ThemePickerPresenter
|
||||
|
||||
@@ -56,19 +60,17 @@ class ThemePickerController(
|
||||
.themePickerModule(ThemePickerModule(this))
|
||||
.build()
|
||||
.inject(this)
|
||||
|
||||
layoutRes = R.layout.theme_picker_controller
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
pager.offscreenPageLimit = 1
|
||||
pager.adapter = themePagerAdapter
|
||||
tabs.pager = pager
|
||||
binding.pager.offscreenPageLimit = 1
|
||||
binding.pager.adapter = themePagerAdapter
|
||||
binding.tabs.pager = binding.pager
|
||||
|
||||
themeAdapter.data = colors.materialColors
|
||||
|
||||
materialColors.layoutManager = LinearLayoutManager(activity)
|
||||
materialColors.adapter = themeAdapter
|
||||
binding.materialColors.layoutManager = LinearLayoutManager(activity)
|
||||
binding.materialColors.adapter = themeAdapter
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@@ -91,7 +93,7 @@ class ThemePickerController(
|
||||
}
|
||||
|
||||
override fun showQksmsPlusSnackbar() {
|
||||
Snackbar.make(contentView, R.string.toast_qksms_plus, Snackbar.LENGTH_LONG).run {
|
||||
Snackbar.make(binding.contentView, R.string.toast_qksms_plus, Snackbar.LENGTH_LONG).run {
|
||||
setAction(R.string.button_more) { viewQksmsPlusSubject.onNext(Unit) }
|
||||
setActionTextColor(colors.theme().theme)
|
||||
show()
|
||||
@@ -100,27 +102,27 @@ class ThemePickerController(
|
||||
|
||||
override fun themeSelected(): Observable<Int> = themeAdapter.colorSelected
|
||||
|
||||
override fun hsvThemeSelected(): Observable<Int> = picker.selectedColor
|
||||
override fun hsvThemeSelected(): Observable<Int> = binding.hsvPicker.picker.selectedColor
|
||||
|
||||
override fun clearHsvThemeClicks(): Observable<*> = clear.clicks()
|
||||
override fun clearHsvThemeClicks(): Observable<*> = binding.hsvPicker.clear.clicks()
|
||||
|
||||
override fun applyHsvThemeClicks(): Observable<*> = apply.clicks()
|
||||
override fun applyHsvThemeClicks(): Observable<*> = binding.hsvPicker.apply.clicks()
|
||||
|
||||
override fun viewQksmsPlusClicks(): Observable<*> = viewQksmsPlusSubject
|
||||
|
||||
override fun render(state: ThemePickerState) {
|
||||
tabs.setRecipientId(state.recipientId)
|
||||
binding.tabs.setRecipientId(state.recipientId)
|
||||
|
||||
hex.setText(Integer.toHexString(state.newColor).takeLast(6))
|
||||
binding.hsvPicker.hex.setText(Integer.toHexString(state.newColor).takeLast(6))
|
||||
|
||||
applyGroup.setVisible(state.applyThemeVisible)
|
||||
apply.setBackgroundTint(state.newColor)
|
||||
apply.setTextColor(state.newTextColor)
|
||||
binding.hsvPicker.applyGroup.setVisible(state.applyThemeVisible)
|
||||
binding.hsvPicker.apply.setBackgroundTint(state.newColor)
|
||||
binding.hsvPicker.apply.setTextColor(state.newTextColor)
|
||||
}
|
||||
|
||||
override fun setCurrentTheme(color: Int) {
|
||||
picker.setColor(color)
|
||||
binding.hsvPicker.picker.setColor(color)
|
||||
themeAdapter.selectedColor = color
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user