Skip to content

Commit

Permalink
#972 - Allow using gallery app to attach photos
Browse files Browse the repository at this point in the history
  • Loading branch information
moezbhatti committed May 18, 2018
1 parent 0da4b96 commit 7888112
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 23 deletions.
7 changes: 4 additions & 3 deletions data/src/main/java/manager/PermissionManagerImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package manager

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.provider.Telephony
import android.support.v4.content.ContextCompat
import javax.inject.Inject
Expand All @@ -35,9 +35,10 @@ class PermissionManagerImpl @Inject constructor(private val context: Context) :
return hasSms() && hasContacts()
}

override fun hasSms(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED
override fun hasSms(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PERMISSION_GRANTED

override fun hasContacts(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
override fun hasContacts(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PERMISSION_GRANTED

override fun hasStorage(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED

}
2 changes: 2 additions & 0 deletions domain/src/main/java/manager/PermissionManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ interface PermissionManager {

fun hasContacts(): Boolean

fun hasStorage(): Boolean

}
1 change: 0 additions & 1 deletion presentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ dependencies {
implementation "com.f2prateek.rx.preferences2:rx-preferences:$rx_preferences_version"
implementation "com.google.android:flexbox:0.3.1"
implementation "com.jakewharton.timber:timber:$timber_version"
implementation "com.mlsdev.rximagepicker:library:2.0.2"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation project(":android-smsmms")
implementation project(':data')
Expand Down
1 change: 1 addition & 0 deletions presentation/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SMS" />

<application
Expand Down
52 changes: 52 additions & 0 deletions presentation/src/main/java/feature/compose/ComposeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@
*/
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.content.res.ColorStateList
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
Expand All @@ -46,10 +53,18 @@ import io.reactivex.subjects.Subject
import kotlinx.android.synthetic.main.compose_activity.*
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
Expand All @@ -72,12 +87,15 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
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 sendIntent by lazy { send.clicks() }
override val backPressedIntent: Subject<Unit> = PublishSubject.create()

private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory)[ComposeViewModel::class.java] }

var cameraDestination: Uri? = null

override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -224,6 +242,30 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
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)
}
Expand All @@ -249,6 +291,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
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)
}
Expand Down
5 changes: 5 additions & 0 deletions presentation/src/main/java/feature/compose/ComposeView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package feature.compose

import android.net.Uri
import android.support.v13.view.inputmethod.InputContentInfoCompat
import common.base.QkView
import io.reactivex.Observable
Expand All @@ -43,10 +44,14 @@ interface ComposeView : QkView<ComposeState> {
val attachIntent: Observable<Unit>
val cameraIntent: Observable<*>
val galleryIntent: Observable<*>
val attachmentSelectedIntent: Observable<Uri>
val inputContentIntent: Observable<InputContentInfoCompat>
val sendIntent: Observable<Unit>

fun clearSelection()
fun requestStoragePermission()
fun requestCamera()
fun requestGallery()
fun setDraft(draft: String)
fun scrollToMessage(id: Long)
val backPressedIntent: Observable<Unit>
Expand Down
36 changes: 17 additions & 19 deletions presentation/src/main/java/feature/compose/ComposeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import android.net.Uri
import android.telephony.PhoneNumberUtils
import android.telephony.SmsMessage
import android.view.inputmethod.EditorInfo
import com.mlsdev.rximagepicker.RxImagePicker
import com.mlsdev.rximagepicker.Sources
import com.moez.QKSMS.R
import com.uber.autodispose.android.lifecycle.scope
import com.uber.autodispose.kotlin.autoDisposable
Expand All @@ -50,6 +48,7 @@ import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.Subject
import io.realm.RealmList
import manager.PermissionManager
import model.Contact
import model.Conversation
import model.Message
Expand All @@ -72,13 +71,14 @@ class ComposeViewModel @Inject constructor(
private val cancelMessage: CancelDelayedMessage,
private val contactFilter: ContactFilter,
private val contactsRepo: ContactRepository,
private val deleteMessages: DeleteMessages,
private val markRead: MarkRead,
private val messageRepo: MessageRepository,
private val navigator: Navigator,
private val syncContacts: ContactSync,
private val sendMessage: SendMessage,
private val permissionManager: PermissionManager,
private val retrySending: RetrySending,
private val markRead: MarkRead,
private val deleteMessages: DeleteMessages
private val sendMessage: SendMessage,
private val syncContacts: ContactSync
) : QkViewModel<ComposeView, ComposeState>(ComposeState(query = intent.extras?.getString("query") ?: "")) {

private var sharedText: String = intent.extras?.getString(Intent.EXTRA_TEXT) ?: ""
Expand Down Expand Up @@ -421,25 +421,23 @@ class ComposeViewModel @Inject constructor(

// Attach a photo from camera
view.cameraIntent
.flatMap { RxImagePicker.with(context).requestImage(Sources.CAMERA) }
.map { uri -> Attachment(uri) }
.withLatestFrom(attachments, { attachment, attachments -> attachments + attachment })
.doOnNext { attachments.onNext(it) }
.autoDisposable(view.scope())
.subscribe { newState { it.copy(attaching = false) } }
.subscribe {
when (permissionManager.hasStorage()) {
true -> view.requestCamera()
false -> view.requestStoragePermission()
}
}

// Attach a photo from gallery
view.galleryIntent
.flatMap { RxImagePicker.with(context).requestImage(Sources.GALLERY) }
.map { uri -> Attachment(uri) }
.withLatestFrom(attachments, { attachment, attachments -> attachments + attachment })
.doOnNext { attachments.onNext(it) }
.autoDisposable(view.scope())
.subscribe { newState { it.copy(attaching = false) } }
.subscribe { view.requestGallery() }

// Attach media from the keyboard
view.inputContentIntent
.map { inputContent -> Attachment(uri = null, inputContent = inputContent) }
// A photo was selected
Observable.merge(
view.attachmentSelectedIntent.map { uri -> Attachment(uri) },
view.inputContentIntent.map { inputContent -> Attachment(inputContent = inputContent) })
.withLatestFrom(attachments, { attachment, attachments -> attachments + attachment })
.doOnNext { attachments.onNext(it) }
.autoDisposable(view.scope())
Expand Down

0 comments on commit 7888112

Please sign in to comment.