From dcbae53112d1d3386525a4151e1da38507388e79 Mon Sep 17 00:00:00 2001 From: Moez Bhatti Date: Fri, 20 Apr 2018 16:51:02 -0400 Subject: [PATCH] #1012 - Show video attachments --- data/src/main/java/mapper/CursorToPartImpl.kt | 3 -- .../main/java/migration/QkRealmMigration.kt | 42 +++++++++++++++++++ .../java/repository/MessageRepositoryImpl.kt | 1 - .../java/util/extensions/MmsPartExtensions.kt | 6 ++- domain/src/main/java/interactor/SaveImage.kt | 4 +- domain/src/main/java/model/MmsPart.kt | 5 ++- .../src/main/java/common/Navigator.kt | 7 ++++ .../src/main/java/common/QKApplication.kt | 14 ++++--- .../main/java/common/widget/MmsPreviewView.kt | 33 +++++++++++---- .../java/feature/compose/ComposeViewModel.kt | 3 +- .../java/feature/compose/MessagesAdapter.kt | 6 +-- .../ConversationMediaAdapter.kt | 2 +- .../java/feature/gallery/GalleryActivity.kt | 2 +- .../main/java/feature/gallery/GalleryState.kt | 4 +- .../java/feature/gallery/GalleryViewModel.kt | 4 +- .../ic_play_circle_filled_white_24dp.xml | 23 ++++++++++ .../main/res/layout/mms_preview_list_item.xml | 39 +++++++++++++++++ 17 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 data/src/main/java/migration/QkRealmMigration.kt create mode 100644 presentation/src/main/res/drawable/ic_play_circle_filled_white_24dp.xml create mode 100644 presentation/src/main/res/layout/mms_preview_list_item.xml diff --git a/data/src/main/java/mapper/CursorToPartImpl.kt b/data/src/main/java/mapper/CursorToPartImpl.kt index 07da3497..c06a4c96 100644 --- a/data/src/main/java/mapper/CursorToPartImpl.kt +++ b/data/src/main/java/mapper/CursorToPartImpl.kt @@ -25,7 +25,6 @@ import android.net.Uri import android.provider.Telephony import model.MmsPart import timber.log.Timber -import util.extensions.isImage import util.extensions.isSmil import util.extensions.isText import java.io.BufferedReader @@ -51,8 +50,6 @@ class CursorToPartImpl @Inject constructor(private val context: Context) : Curso // Do nothing } - isImage() -> image = ContentUris.withAppendedId(CONTENT_URI, id).toString() - isText() -> { text = if (data == null) { from.getString(from.getColumnIndexOrThrow("text")) diff --git a/data/src/main/java/migration/QkRealmMigration.kt b/data/src/main/java/migration/QkRealmMigration.kt new file mode 100644 index 00000000..b93b7160 --- /dev/null +++ b/data/src/main/java/migration/QkRealmMigration.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * 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 . + */ +package migration + +import io.realm.DynamicRealm +import io.realm.RealmMigration + + +class QkRealmMigration : RealmMigration { + + override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { + var version = oldVersion + + if (version == 0L) { + realm.schema.get("MmsPart") + ?.removeField("image") + + version++ + } + + if (version < newVersion) { + throw IllegalStateException("Migration missing from v$oldVersion to v$newVersion") + } + } + +} \ No newline at end of file diff --git a/data/src/main/java/repository/MessageRepositoryImpl.kt b/data/src/main/java/repository/MessageRepositoryImpl.kt index b4989ab9..105193b5 100644 --- a/data/src/main/java/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/repository/MessageRepositoryImpl.kt @@ -206,7 +206,6 @@ class MessageRepositoryImpl @Inject constructor( .where(MmsPart::class.java) .equalTo("messages.threadId", threadId) .contains("type", "image/") - .isNotEmpty("image") .sort("id", Sort.DESCENDING) .findAllAsync() } diff --git a/data/src/main/java/util/extensions/MmsPartExtensions.kt b/data/src/main/java/util/extensions/MmsPartExtensions.kt index 8c6c76da..76245839 100644 --- a/data/src/main/java/util/extensions/MmsPartExtensions.kt +++ b/data/src/main/java/util/extensions/MmsPartExtensions.kt @@ -25,4 +25,8 @@ fun MmsPart.isSmil() = ContentType.APP_SMIL == type fun MmsPart.isImage() = ContentType.isImageType(type) -fun MmsPart.isText() = ContentType.isTextType(type) \ No newline at end of file +fun MmsPart.isVideo() = ContentType.isVideoType(type) + +fun MmsPart.isText() = ContentType.isTextType(type) + +fun MmsPart.hasThumbnails() = isImage() || isVideo() \ No newline at end of file diff --git a/domain/src/main/java/interactor/SaveImage.kt b/domain/src/main/java/interactor/SaveImage.kt index cde05eae..cf84f2b0 100644 --- a/domain/src/main/java/interactor/SaveImage.kt +++ b/domain/src/main/java/interactor/SaveImage.kt @@ -18,7 +18,6 @@ */ package interactor -import androidx.net.toUri import io.reactivex.Flowable import repository.ImageRepository import repository.MessageRepository @@ -32,8 +31,7 @@ class SaveImage @Inject constructor( override fun buildObservable(params: Long): Flowable<*> { return Flowable.just(params) .map { partId -> messageRepo.getPart(partId) } - .map { part -> part.image } - .map { uriString -> uriString.toUri() } + .map { part -> part.getUri() } .doOnNext { uri -> imageRepository.saveImage(uri) } } diff --git a/domain/src/main/java/model/MmsPart.kt b/domain/src/main/java/model/MmsPart.kt index ac8b6eee..4298b582 100644 --- a/domain/src/main/java/model/MmsPart.kt +++ b/domain/src/main/java/model/MmsPart.kt @@ -18,6 +18,7 @@ */ package model +import androidx.net.toUri import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.LinkingObjects @@ -27,11 +28,11 @@ open class MmsPart : RealmObject() { @PrimaryKey var id: Long = 0 var type: String = "" - var text: String? = null - var image: String? = null @LinkingObjects("parts") val messages: RealmResults? = null + fun getUri() = "content://mms/part/$id".toUri() + } \ No newline at end of file diff --git a/presentation/src/main/java/common/Navigator.kt b/presentation/src/main/java/common/Navigator.kt index e0678638..95243590 100644 --- a/presentation/src/main/java/common/Navigator.kt +++ b/presentation/src/main/java/common/Navigator.kt @@ -119,6 +119,13 @@ class Navigator @Inject constructor(private val context: Context, private val no startActivity(intent) } + fun showVideo(uri: Uri, type: String) { + val intent = Intent(Intent.ACTION_VIEW, uri) + .setDataAndType(uri, type) + + startActivityExternal(intent) + } + /** * Shows the attachment full-screen * The transitionName for the view should be the id of the image being displayed diff --git a/presentation/src/main/java/common/QKApplication.kt b/presentation/src/main/java/common/QKApplication.kt index 9101d3a8..60a1072a 100644 --- a/presentation/src/main/java/common/QKApplication.kt +++ b/presentation/src/main/java/common/QKApplication.kt @@ -32,6 +32,7 @@ import injection.appComponent import io.realm.Realm import io.realm.RealmConfiguration import manager.AnalyticsManager +import migration.QkRealmMigration import timber.log.Timber import javax.inject.Inject @@ -52,17 +53,18 @@ class QKApplication : Application() { appVersion = BuildConfig.VERSION_NAME }) + Realm.init(this) + Realm.setDefaultConfiguration(RealmConfiguration.Builder() + .compactOnLaunch() + .migration(QkRealmMigration()) + .schemaVersion(1) + .build()) + AppComponentManager.init(this) appComponent.inject(this) nightModeManager.updateCurrentTheme() - Realm.init(this) - Realm.setDefaultConfiguration(RealmConfiguration.Builder() - .compactOnLaunch() - .deleteRealmIfMigrationNeeded() - .build()) - val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", diff --git a/presentation/src/main/java/common/widget/MmsPreviewView.kt b/presentation/src/main/java/common/widget/MmsPreviewView.kt index 2a7e55da..e3f9a082 100644 --- a/presentation/src/main/java/common/widget/MmsPreviewView.kt +++ b/presentation/src/main/java/common/widget/MmsPreviewView.kt @@ -20,19 +20,26 @@ package common.widget import android.content.Context import android.util.AttributeSet -import android.widget.ImageView +import android.view.LayoutInflater import android.widget.LinearLayout +import com.moez.QKSMS.R import common.Navigator import common.util.GlideApp import common.util.extensions.setVisible import injection.appComponent +import kotlinx.android.synthetic.main.mms_preview_list_item.view.* import model.MmsPart +import util.extensions.hasThumbnails +import util.extensions.isImage +import util.extensions.isVideo import javax.inject.Inject class MmsPreviewView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) { @Inject lateinit var navigator: Navigator + private val layoutInflater = LayoutInflater.from(context) + var parts: List = ArrayList() set(value) { field = value @@ -48,19 +55,27 @@ class MmsPreviewView @JvmOverloads constructor(context: Context, attrs: Attribut } private fun updateView() { - val images = parts.filter { it.image != null } - setVisible(images.isNotEmpty()) + val media = parts.filter { it.hasThumbnails() } + setVisible(media.isNotEmpty()) if (childCount > 0) { removeAllViews() } - images.forEach { image -> - addView(ImageView(context).apply { - layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - GlideApp.with(context).load(image.image).fitCenter().into(this) - setOnClickListener { navigator.showImage(image.id) } - }) + media.forEach { part -> + val view = layoutInflater.inflate(R.layout.mms_preview_list_item, this, false) + view.video.setVisible(part.isVideo()) + + GlideApp.with(context).load(part.getUri()).fitCenter().into(view.thumbnail) + + setOnClickListener { + when { + part.isImage() -> navigator.showImage(part.id) + part.isVideo() -> navigator.showVideo(part.getUri(), part.type) + } + } + + addView(view) } } diff --git a/presentation/src/main/java/feature/compose/ComposeViewModel.kt b/presentation/src/main/java/feature/compose/ComposeViewModel.kt index 43ca5f0d..dc03a473 100644 --- a/presentation/src/main/java/feature/compose/ComposeViewModel.kt +++ b/presentation/src/main/java/feature/compose/ComposeViewModel.kt @@ -25,7 +25,6 @@ import android.telephony.PhoneNumberUtils import android.telephony.SmsMessage import android.view.KeyEvent import android.view.inputmethod.EditorInfo -import androidx.net.toUri import com.mlsdev.rximagepicker.RxImagePicker import com.mlsdev.rximagepicker.Sources import com.moez.QKSMS.R @@ -323,7 +322,7 @@ class ComposeViewModel(intent: Intent) : QkViewModel( } menuForward.actionId -> { - val images = message.parts.filter { it.isImage() }.mapNotNull { it.image?.toUri() } + val images = message.parts.filter { it.isImage() }.mapNotNull { it.getUri() } navigator.showCompose(message.body, images) } diff --git a/presentation/src/main/java/feature/compose/MessagesAdapter.kt b/presentation/src/main/java/feature/compose/MessagesAdapter.kt index 61c16507..49005fc0 100644 --- a/presentation/src/main/java/feature/compose/MessagesAdapter.kt +++ b/presentation/src/main/java/feature/compose/MessagesAdapter.kt @@ -44,7 +44,7 @@ import kotlinx.android.synthetic.main.message_list_item_in.view.* import model.Conversation import model.Message import model.Recipient -import util.extensions.isImage +import util.extensions.hasThumbnails import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -139,7 +139,7 @@ class MessagesAdapter @Inject constructor( view.subject.setVisible(view.subject.text.isNotBlank()) view.body.text = message.getText() - view.body.setVisible(message.isSms() || view.body.text.isNotEmpty() || message.parts.all { it.image == null }) + view.body.setVisible(message.isSms() || view.body.text.isNotEmpty() || message.parts.all { !it.hasThumbnails() }) view.timestamp.text = dateFormatter.getMessageTimestamp(message.date) @@ -219,7 +219,7 @@ class MessagesAdapter @Inject constructor( } // If we're showing any thumbnails, set clipToOutline to true - val hasImages = message.parts.filter { it.isImage() }.any() + val hasImages = message.parts.filter { it.hasThumbnails() }.any() view.messageBackground.clipToOutline = hasImages // setClipToOutline doesn't work unless all of the corners have the same radius, so we have diff --git a/presentation/src/main/java/feature/conversationinfo/ConversationMediaAdapter.kt b/presentation/src/main/java/feature/conversationinfo/ConversationMediaAdapter.kt index a1a0cc5d..d55f71f5 100644 --- a/presentation/src/main/java/feature/conversationinfo/ConversationMediaAdapter.kt +++ b/presentation/src/main/java/feature/conversationinfo/ConversationMediaAdapter.kt @@ -57,7 +57,7 @@ class ConversationMediaAdapter @Inject constructor( val view = holder.itemView GlideApp.with(context) - .load(part.image) + .load(part.getUri()) .fitCenter() .into(view.thumbnail) diff --git a/presentation/src/main/java/feature/gallery/GalleryActivity.kt b/presentation/src/main/java/feature/gallery/GalleryActivity.kt index 2dd7755e..5b9ddd05 100644 --- a/presentation/src/main/java/feature/gallery/GalleryActivity.kt +++ b/presentation/src/main/java/feature/gallery/GalleryActivity.kt @@ -85,7 +85,7 @@ class GalleryActivity : QkActivity(), GalleryView { if (image.drawable == null) { GlideApp.with(this) - .load(state.imagePath) + .load(state.imageUri) .dontAnimate() .listener(GlideCompletionListener { startPostponedEnterTransition() diff --git a/presentation/src/main/java/feature/gallery/GalleryState.kt b/presentation/src/main/java/feature/gallery/GalleryState.kt index 08c0ffa7..08726bc2 100644 --- a/presentation/src/main/java/feature/gallery/GalleryState.kt +++ b/presentation/src/main/java/feature/gallery/GalleryState.kt @@ -18,8 +18,10 @@ */ package feature.gallery +import android.net.Uri + data class GalleryState( val navigationVisible: Boolean = true, val title: String = "", - val imagePath: String = "" + val imageUri: Uri? = null ) \ No newline at end of file diff --git a/presentation/src/main/java/feature/gallery/GalleryViewModel.kt b/presentation/src/main/java/feature/gallery/GalleryViewModel.kt index 48759451..5858223d 100644 --- a/presentation/src/main/java/feature/gallery/GalleryViewModel.kt +++ b/presentation/src/main/java/feature/gallery/GalleryViewModel.kt @@ -49,8 +49,8 @@ class GalleryViewModel(intent: Intent) : QkViewModel( disposables += partIdFlowable .mapNotNull { partId -> messageRepo.getPart(partId) } - .mapNotNull { part -> part.image } - .subscribe { path -> newState { it.copy(imagePath = path) } } + .mapNotNull { part -> part.getUri() } + .subscribe { uri -> newState { it.copy(imageUri = uri) } } disposables += partIdFlowable .mapNotNull { partId -> messageRepo.getMessageForPart(partId) } diff --git a/presentation/src/main/res/drawable/ic_play_circle_filled_white_24dp.xml b/presentation/src/main/res/drawable/ic_play_circle_filled_white_24dp.xml new file mode 100644 index 00000000..0ac7f3a1 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_play_circle_filled_white_24dp.xml @@ -0,0 +1,23 @@ + + + + diff --git a/presentation/src/main/res/layout/mms_preview_list_item.xml b/presentation/src/main/res/layout/mms_preview_list_item.xml new file mode 100644 index 00000000..6402ab80 --- /dev/null +++ b/presentation/src/main/res/layout/mms_preview_list_item.xml @@ -0,0 +1,39 @@ + + + + + + + + + \ No newline at end of file