forked from upstream-mirrors/quik
d6fa04f56d
Potentially related to #1079
284 lines
11 KiB
Kotlin
284 lines
11 KiB
Kotlin
/*
|
|
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
|
*
|
|
* This file is part of QKSMS.
|
|
*
|
|
* QKSMS is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* QKSMS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package feature.compose
|
|
|
|
import android.Manifest
|
|
import android.app.Activity
|
|
import android.arch.lifecycle.ViewModelProvider
|
|
import android.arch.lifecycle.ViewModelProviders
|
|
import android.content.ContentValues
|
|
import android.content.Intent
|
|
import android.graphics.PorterDuff
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.provider.MediaStore
|
|
import android.support.v4.app.ActivityCompat
|
|
import android.support.v7.widget.LinearLayoutManager
|
|
import android.view.Menu
|
|
import android.view.MenuItem
|
|
import com.google.android.flexbox.FlexboxLayoutManager
|
|
import com.jakewharton.rxbinding2.view.clicks
|
|
import com.jakewharton.rxbinding2.widget.textChanges
|
|
import com.moez.QKSMS.R
|
|
import com.uber.autodispose.android.lifecycle.scope
|
|
import com.uber.autodispose.kotlin.autoDisposable
|
|
import common.base.QkThemedActivity
|
|
import common.util.extensions.autoScrollToStart
|
|
import common.util.extensions.scrapViews
|
|
import common.util.extensions.setBackgroundTint
|
|
import common.util.extensions.setTint
|
|
import common.util.extensions.setVisible
|
|
import common.util.extensions.showKeyboard
|
|
import dagger.android.AndroidInjection
|
|
import io.reactivex.Observable
|
|
import io.reactivex.subjects.PublishSubject
|
|
import io.reactivex.subjects.Subject
|
|
import kotlinx.android.synthetic.main.compose_activity.*
|
|
import model.Attachment
|
|
import model.Contact
|
|
import model.Message
|
|
import java.text.SimpleDateFormat
|
|
import java.util.*
|
|
import javax.inject.Inject
|
|
|
|
|
|
class ComposeActivity : QkThemedActivity(), ComposeView {
|
|
|
|
companion object {
|
|
const val CAMERA_REQUEST_CODE = 0
|
|
const val GALLERY_REQUEST_CODE = 1
|
|
}
|
|
|
|
@Inject lateinit var attachmentAdapter: AttachmentAdapter
|
|
@Inject lateinit var chipsAdapter: ChipsAdapter
|
|
@Inject lateinit var contactsAdapter: ContactAdapter
|
|
@Inject lateinit var messageAdapter: MessagesAdapter
|
|
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
|
|
|
override val activityVisibleIntent: Subject<Boolean> = PublishSubject.create()
|
|
override val queryChangedIntent: Observable<CharSequence> by lazy { chipsAdapter.textChanges }
|
|
override val queryBackspaceIntent: Observable<*> by lazy { chipsAdapter.backspaces }
|
|
override val queryEditorActionIntent: Observable<Int> by lazy { chipsAdapter.actions }
|
|
override val chipSelectedIntent: Subject<Contact> by lazy { contactsAdapter.contactSelected }
|
|
override val chipDeletedIntent: Subject<Contact> by lazy { chipsAdapter.chipDeleted }
|
|
override val menuReadyIntent: Observable<Unit> = menu.map { Unit }
|
|
override val optionsItemIntent: Subject<Int> = PublishSubject.create()
|
|
override val sendAsGroupIntent by lazy { sendAsGroupBackground.clicks() }
|
|
override val messageClickIntent: Subject<Message> by lazy { messageAdapter.clicks }
|
|
override val messagesSelectedIntent by lazy { messageAdapter.selectionChanges }
|
|
override val cancelSendingIntent: Subject<Message> by lazy { messageAdapter.cancelSending }
|
|
override val attachmentDeletedIntent: Subject<Attachment> by lazy { attachmentAdapter.attachmentDeleted }
|
|
override val textChangedIntent by lazy { message.textChanges() }
|
|
override val attachIntent by lazy { attach.clicks() }
|
|
override val cameraIntent by lazy { camera.clicks() }
|
|
override val galleryIntent by lazy { gallery.clicks() }
|
|
override val attachmentSelectedIntent: Subject<Uri> = PublishSubject.create()
|
|
override val inputContentIntent by lazy { message.inputContentSelected }
|
|
override val changeSimIntent by lazy { sim.clicks() }
|
|
override val sendIntent by lazy { send.clicks() }
|
|
override val backPressedIntent: Subject<Unit> = PublishSubject.create()
|
|
|
|
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory)[ComposeViewModel::class.java] }
|
|
|
|
private var cameraDestination: Uri? = null
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
AndroidInjection.inject(this)
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.compose_activity)
|
|
showBackButton(true)
|
|
viewModel.bindView(this)
|
|
|
|
chipsAdapter.view = chips
|
|
|
|
contacts.itemAnimator = null
|
|
chips.itemAnimator = null
|
|
chips.layoutManager = FlexboxLayoutManager(this)
|
|
|
|
val layoutManager = LinearLayoutManager(this).apply { stackFromEnd = true }
|
|
|
|
messageAdapter.autoScrollToStart(messageList)
|
|
messageAdapter.emptyView = messagesEmpty
|
|
|
|
messageList.setHasFixedSize(true)
|
|
messageList.layoutManager = layoutManager
|
|
messageList.adapter = messageAdapter
|
|
|
|
attachments.adapter = attachmentAdapter
|
|
|
|
messageBackground.backgroundTintMode = PorterDuff.Mode.MULTIPLY
|
|
message.supportsInputContent = true
|
|
|
|
theme
|
|
.doOnNext { loading.setTint(it.theme) }
|
|
.doOnNext { send.setBackgroundTint(it.theme) }
|
|
.doOnNext { send.setTint(it.textPrimary) }
|
|
.doOnNext { messageAdapter.theme = it }
|
|
.autoDisposable(scope())
|
|
.subscribe { messageList.scrapViews() }
|
|
|
|
window.callback = ComposeWindowCallback(window.callback, this)
|
|
}
|
|
|
|
override fun onStart() {
|
|
super.onStart()
|
|
activityVisibleIntent.onNext(true)
|
|
}
|
|
|
|
override fun onPause() {
|
|
super.onPause()
|
|
activityVisibleIntent.onNext(false)
|
|
}
|
|
|
|
override fun render(state: ComposeState) {
|
|
if (state.hasError) {
|
|
finish()
|
|
return
|
|
}
|
|
|
|
threadId.onNext(state.selectedConversation)
|
|
|
|
title = when {
|
|
state.selectedMessages > 0 -> getString(R.string.compose_title_selected, state.selectedMessages)
|
|
state.query.isNotEmpty() -> state.query
|
|
else -> state.conversationtitle
|
|
}
|
|
|
|
toolbarSubtitle.setVisible(state.query.isNotEmpty())
|
|
toolbarSubtitle.text = getString(R.string.compose_subtitle_results, state.searchSelectionPosition, state.searchResults)
|
|
|
|
toolbarTitle.setVisible(!state.editingMode)
|
|
chips.setVisible(state.editingMode)
|
|
contacts.setVisible(state.contactsVisible)
|
|
composeBar.setVisible(!state.contactsVisible && !state.loading)
|
|
|
|
// Don't set the adapters unless needed
|
|
if (state.editingMode && chips.adapter == null) chips.adapter = chipsAdapter
|
|
if (state.editingMode && contacts.adapter == null) contacts.adapter = contactsAdapter
|
|
|
|
toolbar.menu.findItem(R.id.call)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty()
|
|
toolbar.menu.findItem(R.id.info)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty()
|
|
toolbar.menu.findItem(R.id.copy)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
|
toolbar.menu.findItem(R.id.delete)?.isVisible = !state.editingMode && state.selectedMessages > 0
|
|
toolbar.menu.findItem(R.id.forward)?.isVisible = !state.editingMode && state.selectedMessages == 1
|
|
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()
|
|
|
|
if (chipsAdapter.data.isEmpty() && state.selectedContacts.isNotEmpty()) {
|
|
message.showKeyboard()
|
|
}
|
|
|
|
chipsAdapter.data = state.selectedContacts
|
|
contactsAdapter.data = state.contacts
|
|
|
|
loading.setVisible(state.loading)
|
|
|
|
sendAsGroup.setVisible(state.editingMode && state.selectedContacts.size >= 2)
|
|
sendAsGroupSwitch.isChecked = state.sendAsGroup
|
|
|
|
messageList.setVisible(state.sendAsGroup)
|
|
messageAdapter.data = state.messages
|
|
messageAdapter.highlight = state.searchSelectionId
|
|
|
|
attachments.setVisible(state.attachments.isNotEmpty())
|
|
attachmentAdapter.data = state.attachments
|
|
|
|
attach.animate().rotation(if (state.attaching) 45f else 0f).start()
|
|
camera.setVisible(state.attaching)
|
|
gallery.setVisible(state.attaching)
|
|
|
|
counter.text = state.remaining
|
|
counter.setVisible(counter.text.isNotBlank())
|
|
|
|
sim.setVisible(state.subscription != null)
|
|
simIndex.text = "${state.subscription?.simSlotIndex?.plus(1)}"
|
|
|
|
send.isEnabled = state.canSend
|
|
send.imageAlpha = if (state.canSend) 255 else 128
|
|
}
|
|
|
|
override fun clearSelection() {
|
|
messageAdapter.clearSelection()
|
|
}
|
|
|
|
override fun requestStoragePermission() {
|
|
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 0)
|
|
}
|
|
|
|
override fun requestCamera() {
|
|
cameraDestination = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
|
.let { timestamp -> ContentValues().apply { put(MediaStore.Images.Media.TITLE, timestamp) } }
|
|
.let { cv -> contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cv) }
|
|
|
|
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
|
.putExtra(MediaStore.EXTRA_OUTPUT, cameraDestination)
|
|
startActivityForResult(intent, CAMERA_REQUEST_CODE)
|
|
}
|
|
|
|
override fun requestGallery() {
|
|
val intent = Intent(Intent.ACTION_PICK)
|
|
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
|
.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
.putExtra(Intent.EXTRA_LOCAL_ONLY, false)
|
|
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
.setType("image/*")
|
|
startActivityForResult(intent, GALLERY_REQUEST_CODE)
|
|
}
|
|
|
|
override fun setDraft(draft: String) {
|
|
message.setText(draft)
|
|
}
|
|
|
|
override fun scrollToMessage(id: Long) {
|
|
messageAdapter.data?.second
|
|
?.indexOfLast { message -> message.id == id }
|
|
?.takeIf { position -> position != -1 }
|
|
?.let(messageList::scrollToPosition)
|
|
}
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
|
menuInflater.inflate(R.menu.compose, menu)
|
|
return super.onCreateOptionsMenu(menu)
|
|
}
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
optionsItemIntent.onNext(item.itemId)
|
|
return true
|
|
}
|
|
|
|
override fun getColoredMenuItems(): List<Int> {
|
|
return super.getColoredMenuItems() + R.id.call
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
when (requestCode) {
|
|
CAMERA_REQUEST_CODE -> cameraDestination
|
|
GALLERY_REQUEST_CODE -> data?.data
|
|
else -> null
|
|
}?.let(attachmentSelectedIntent::onNext)
|
|
}
|
|
}
|
|
|
|
override fun onBackPressed() {
|
|
backPressedIntent.onNext(Unit)
|
|
}
|
|
|
|
} |