Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +20 −15 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.ContactsContract import android.provider.MediaStore import android.text.format.DateFormat import android.view.Menu Loading Loading @@ -68,8 +69,9 @@ import javax.inject.Inject class ComposeActivity : QkThemedActivity(), ComposeView { companion object { const val CAMERA_REQUEST_CODE = 0 const val GALLERY_REQUEST_CODE = 1 private const val CAMERA_REQUEST_CODE = 0 private const val GALLERY_REQUEST_CODE = 1 private const val CONTACT_REQUEST_CODE = 2 } @Inject lateinit var attachmentAdapter: AttachmentAdapter Loading Loading @@ -97,7 +99,9 @@ class ComposeActivity : QkThemedActivity(), ComposeView { override val cameraIntent by lazy { Observable.merge(camera.clicks(), cameraLabel.clicks()) } override val galleryIntent by lazy { Observable.merge(gallery.clicks(), galleryLabel.clicks()) } override val scheduleIntent by lazy { Observable.merge(schedule.clicks(), scheduleLabel.clicks()) } override val attachContactIntent by lazy { Observable.merge(contact.clicks(), contactLabel.clicks()) } override val attachmentSelectedIntent: Subject<Uri> = PublishSubject.create() override val contactSelectedIntent: Subject<Uri> = PublishSubject.create() override val inputContentIntent by lazy { message.inputContentSelected } override val scheduleSelectedIntent: Subject<Long> = PublishSubject.create() override val changeSimIntent by lazy { sim.clicks() } Loading Loading @@ -232,9 +236,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { send.imageAlpha = if (state.canSend) 255 else 128 } override fun clearSelection() { messageAdapter.clearSelection() } override fun clearSelection() = messageAdapter.clearSelection() override fun showDetails(details: String) { AlertDialog.Builder(this) Loading Loading @@ -268,6 +270,13 @@ class ComposeActivity : QkThemedActivity(), ComposeView { }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)).show() } override fun requestContact() { val intent = Intent(Intent.ACTION_PICK) .setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE) startActivityForResult(Intent.createChooser(intent, null), CONTACT_REQUEST_CODE) } override fun requestCamera() { cameraDestination = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) .let { timestamp -> ContentValues().apply { put(MediaStore.Images.Media.TITLE, timestamp) } } Loading @@ -288,9 +297,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { startActivityForResult(Intent.createChooser(intent, null), GALLERY_REQUEST_CODE) } override fun setDraft(draft: String) { message.setText(draft) } override fun setDraft(draft: String) = message.setText(draft) override fun scrollToMessage(id: Long) { messageAdapter.data?.second Loading Loading @@ -323,15 +330,13 @@ class ComposeActivity : QkThemedActivity(), ComposeView { 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) CAMERA_REQUEST_CODE -> cameraDestination?.let(attachmentSelectedIntent::onNext) GALLERY_REQUEST_CODE -> data?.data?.let(attachmentSelectedIntent::onNext) CONTACT_REQUEST_CODE -> data?.data?.let(contactSelectedIntent::onNext) } } override fun onBackPressed() { backPressedIntent.onNext(Unit) } override fun onBackPressed() = backPressedIntent.onNext(Unit) } No newline at end of file presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +3 −0 Original line number Diff line number Diff line Loading @@ -48,7 +48,9 @@ interface ComposeView : QkView<ComposeState> { val cameraIntent: Observable<*> val galleryIntent: Observable<*> val scheduleIntent: Observable<*> val attachContactIntent: Observable<*> val attachmentSelectedIntent: Observable<Uri> val contactSelectedIntent: Observable<Uri> val inputContentIntent: Observable<InputContentInfoCompat> val scheduleSelectedIntent: Observable<Long> val scheduleCancelIntent: Observable<*> Loading @@ -64,6 +66,7 @@ interface ComposeView : QkView<ComposeState> { fun requestCamera() fun requestGallery() fun requestDatePicker() fun requestContact() fun setDraft(draft: String) fun scrollToMessage(id: Long) fun showQksmsPlusSnackbar(@StringRes message: Int) Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +32 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.moez.QKSMS.feature.compose import android.content.Context import android.net.Uri import android.provider.ContactsContract import android.telephony.PhoneNumberUtils import android.telephony.SmsMessage import android.view.inputmethod.EditorInfo Loading Loading @@ -68,10 +70,12 @@ import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import io.realm.RealmList import timber.log.Timber import java.util.* import javax.inject.Inject import javax.inject.Named class ComposeViewModel @Inject constructor( @Named("query") private val query: String, @Named("threadId") private val threadId: Long, Loading Loading @@ -519,6 +523,21 @@ class ComposeViewModel @Inject constructor( .autoDisposable(view.scope()) .subscribe { scheduled -> newState { copy(scheduled = scheduled) } } // Attach a contact view.attachContactIntent .doOnNext { newState { copy(attaching = false) } } .autoDisposable(view.scope()) .subscribe { view.requestContact() } // Contact was selected for attachment view.contactSelectedIntent .map(::vCard) .autoDisposable(view.scope()) .subscribe({}, { error -> context.makeToast(R.string.compose_contact_error) Timber.w(error) }) // Detach a photo view.attachmentDeletedIntent .withLatestFrom(attachments) { bitmap, attachments -> attachments.filter { it !== bitmap } } Loading Loading @@ -678,4 +697,17 @@ class ComposeViewModel @Inject constructor( } private fun vCard(contactData: Uri): String? { val lookupKey = context.contentResolver.query(contactData, null, null, null, null)?.use { cursor -> cursor.moveToFirst() cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)) } val vCardUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey) return context.contentResolver.openAssetFileDescriptor(vCardUri, "r") ?.createInputStream() ?.readBytes() ?.let { bytes -> String(bytes) } } } No newline at end of file presentation/src/main/res/layout/compose_activity.xml +36 −1 Original line number Diff line number Diff line Loading @@ -397,7 +397,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" app:constraint_referenced_ids="schedule, scheduleLabel, gallery, galleryLabel, camera, cameraLabel, attachingBackground" /> app:constraint_referenced_ids="contact, contactLabel, schedule, scheduleLabel, gallery, galleryLabel, camera, cameraLabel, attachingBackground" /> <View android:id="@+id/attachingBackground" Loading @@ -410,6 +411,40 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/contact" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginBottom="12dp" android:background="@drawable/circle" android:backgroundTint="?attr/bubbleColor" android:contentDescription="@string/compose_contact_cd" android:elevation="4dp" android:padding="10dp" android:src="@drawable/ic_person_black_24dp" android:tint="?android:attr/textColorSecondary" android:visibility="visible" app:layout_constraintBottom_toTopOf="@id/schedule" app:layout_constraintEnd_toEndOf="@id/attach" app:layout_constraintStart_toStartOf="@id/attach" /> <com.moez.QKSMS.common.widget.QkTextView android:id="@+id/contactLabel" android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginStart="8dp" android:background="@drawable/rounded_rectangle_4dp" android:backgroundTint="?attr/bubbleColor" android:elevation="4dp" android:gravity="center_vertical" android:paddingEnd="8dp" android:paddingStart="8dp" android:text="@string/compose_contact_cd" android:textColor="?android:attr/textColorPrimary" app:layout_constraintBottom_toBottomOf="@id/contact" app:layout_constraintStart_toEndOf="@id/contact" app:layout_constraintTop_toTopOf="@id/contact" /> <ImageView android:id="@+id/schedule" android:layout_width="40dp" Loading presentation/src/main/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,8 @@ <string name="compose_gallery_cd">Attach a photo</string> <string name="compose_camera_cd">Take a photo</string> <string name="compose_schedule_cd">Schedule message</string> <string name="compose_contact_cd">Attach a contact</string> <string name="compose_contact_error">Error reading contact</string> <string name="compose_sim_cd">%s selected, change SIM card</string> <string name="compose_send_cd">Send message</string> Loading Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +20 −15 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.ContactsContract import android.provider.MediaStore import android.text.format.DateFormat import android.view.Menu Loading Loading @@ -68,8 +69,9 @@ import javax.inject.Inject class ComposeActivity : QkThemedActivity(), ComposeView { companion object { const val CAMERA_REQUEST_CODE = 0 const val GALLERY_REQUEST_CODE = 1 private const val CAMERA_REQUEST_CODE = 0 private const val GALLERY_REQUEST_CODE = 1 private const val CONTACT_REQUEST_CODE = 2 } @Inject lateinit var attachmentAdapter: AttachmentAdapter Loading Loading @@ -97,7 +99,9 @@ class ComposeActivity : QkThemedActivity(), ComposeView { override val cameraIntent by lazy { Observable.merge(camera.clicks(), cameraLabel.clicks()) } override val galleryIntent by lazy { Observable.merge(gallery.clicks(), galleryLabel.clicks()) } override val scheduleIntent by lazy { Observable.merge(schedule.clicks(), scheduleLabel.clicks()) } override val attachContactIntent by lazy { Observable.merge(contact.clicks(), contactLabel.clicks()) } override val attachmentSelectedIntent: Subject<Uri> = PublishSubject.create() override val contactSelectedIntent: Subject<Uri> = PublishSubject.create() override val inputContentIntent by lazy { message.inputContentSelected } override val scheduleSelectedIntent: Subject<Long> = PublishSubject.create() override val changeSimIntent by lazy { sim.clicks() } Loading Loading @@ -232,9 +236,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { send.imageAlpha = if (state.canSend) 255 else 128 } override fun clearSelection() { messageAdapter.clearSelection() } override fun clearSelection() = messageAdapter.clearSelection() override fun showDetails(details: String) { AlertDialog.Builder(this) Loading Loading @@ -268,6 +270,13 @@ class ComposeActivity : QkThemedActivity(), ComposeView { }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)).show() } override fun requestContact() { val intent = Intent(Intent.ACTION_PICK) .setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE) startActivityForResult(Intent.createChooser(intent, null), CONTACT_REQUEST_CODE) } override fun requestCamera() { cameraDestination = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) .let { timestamp -> ContentValues().apply { put(MediaStore.Images.Media.TITLE, timestamp) } } Loading @@ -288,9 +297,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { startActivityForResult(Intent.createChooser(intent, null), GALLERY_REQUEST_CODE) } override fun setDraft(draft: String) { message.setText(draft) } override fun setDraft(draft: String) = message.setText(draft) override fun scrollToMessage(id: Long) { messageAdapter.data?.second Loading Loading @@ -323,15 +330,13 @@ class ComposeActivity : QkThemedActivity(), ComposeView { 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) CAMERA_REQUEST_CODE -> cameraDestination?.let(attachmentSelectedIntent::onNext) GALLERY_REQUEST_CODE -> data?.data?.let(attachmentSelectedIntent::onNext) CONTACT_REQUEST_CODE -> data?.data?.let(contactSelectedIntent::onNext) } } override fun onBackPressed() { backPressedIntent.onNext(Unit) } override fun onBackPressed() = backPressedIntent.onNext(Unit) } No newline at end of file
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +3 −0 Original line number Diff line number Diff line Loading @@ -48,7 +48,9 @@ interface ComposeView : QkView<ComposeState> { val cameraIntent: Observable<*> val galleryIntent: Observable<*> val scheduleIntent: Observable<*> val attachContactIntent: Observable<*> val attachmentSelectedIntent: Observable<Uri> val contactSelectedIntent: Observable<Uri> val inputContentIntent: Observable<InputContentInfoCompat> val scheduleSelectedIntent: Observable<Long> val scheduleCancelIntent: Observable<*> Loading @@ -64,6 +66,7 @@ interface ComposeView : QkView<ComposeState> { fun requestCamera() fun requestGallery() fun requestDatePicker() fun requestContact() fun setDraft(draft: String) fun scrollToMessage(id: Long) fun showQksmsPlusSnackbar(@StringRes message: Int) Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +32 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.moez.QKSMS.feature.compose import android.content.Context import android.net.Uri import android.provider.ContactsContract import android.telephony.PhoneNumberUtils import android.telephony.SmsMessage import android.view.inputmethod.EditorInfo Loading Loading @@ -68,10 +70,12 @@ import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import io.realm.RealmList import timber.log.Timber import java.util.* import javax.inject.Inject import javax.inject.Named class ComposeViewModel @Inject constructor( @Named("query") private val query: String, @Named("threadId") private val threadId: Long, Loading Loading @@ -519,6 +523,21 @@ class ComposeViewModel @Inject constructor( .autoDisposable(view.scope()) .subscribe { scheduled -> newState { copy(scheduled = scheduled) } } // Attach a contact view.attachContactIntent .doOnNext { newState { copy(attaching = false) } } .autoDisposable(view.scope()) .subscribe { view.requestContact() } // Contact was selected for attachment view.contactSelectedIntent .map(::vCard) .autoDisposable(view.scope()) .subscribe({}, { error -> context.makeToast(R.string.compose_contact_error) Timber.w(error) }) // Detach a photo view.attachmentDeletedIntent .withLatestFrom(attachments) { bitmap, attachments -> attachments.filter { it !== bitmap } } Loading Loading @@ -678,4 +697,17 @@ class ComposeViewModel @Inject constructor( } private fun vCard(contactData: Uri): String? { val lookupKey = context.contentResolver.query(contactData, null, null, null, null)?.use { cursor -> cursor.moveToFirst() cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)) } val vCardUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey) return context.contentResolver.openAssetFileDescriptor(vCardUri, "r") ?.createInputStream() ?.readBytes() ?.let { bytes -> String(bytes) } } } No newline at end of file
presentation/src/main/res/layout/compose_activity.xml +36 −1 Original line number Diff line number Diff line Loading @@ -397,7 +397,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" app:constraint_referenced_ids="schedule, scheduleLabel, gallery, galleryLabel, camera, cameraLabel, attachingBackground" /> app:constraint_referenced_ids="contact, contactLabel, schedule, scheduleLabel, gallery, galleryLabel, camera, cameraLabel, attachingBackground" /> <View android:id="@+id/attachingBackground" Loading @@ -410,6 +411,40 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/contact" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginBottom="12dp" android:background="@drawable/circle" android:backgroundTint="?attr/bubbleColor" android:contentDescription="@string/compose_contact_cd" android:elevation="4dp" android:padding="10dp" android:src="@drawable/ic_person_black_24dp" android:tint="?android:attr/textColorSecondary" android:visibility="visible" app:layout_constraintBottom_toTopOf="@id/schedule" app:layout_constraintEnd_toEndOf="@id/attach" app:layout_constraintStart_toStartOf="@id/attach" /> <com.moez.QKSMS.common.widget.QkTextView android:id="@+id/contactLabel" android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginStart="8dp" android:background="@drawable/rounded_rectangle_4dp" android:backgroundTint="?attr/bubbleColor" android:elevation="4dp" android:gravity="center_vertical" android:paddingEnd="8dp" android:paddingStart="8dp" android:text="@string/compose_contact_cd" android:textColor="?android:attr/textColorPrimary" app:layout_constraintBottom_toBottomOf="@id/contact" app:layout_constraintStart_toEndOf="@id/contact" app:layout_constraintTop_toTopOf="@id/contact" /> <ImageView android:id="@+id/schedule" android:layout_width="40dp" Loading
presentation/src/main/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,8 @@ <string name="compose_gallery_cd">Attach a photo</string> <string name="compose_camera_cd">Take a photo</string> <string name="compose_schedule_cd">Schedule message</string> <string name="compose_contact_cd">Attach a contact</string> <string name="compose_contact_error">Error reading contact</string> <string name="compose_sim_cd">%s selected, change SIM card</string> <string name="compose_send_cd">Send message</string> Loading