Attachements: introduce structure for preview, cropping and compressing

This commit is contained in:
Ganard 2020-02-04 16:35:04 +01:00 committed by Benoit Marty
parent 5808c1de22
commit a26e959430
14 changed files with 448 additions and 0 deletions

View file

@ -34,6 +34,8 @@ allprojects {
includeGroupByRegex "com\\.github\\.jaiselrahman"
// And monarchy
includeGroupByRegex "com\\.github\\.Zhuinden"
// And ucrop
includeGroupByRegex "com\\.github\\.yalantis"
}
}
maven {

View file

@ -341,6 +341,7 @@ dependencies {
implementation "com.github.bumptech.glide:glide:$glide_version"
kapt "com.github.bumptech.glide:compiler:$glide_version"
implementation 'com.danikula:videocache:2.7.1'
implementation 'com.github.yalantis:ucrop:2.2.4'
// Badge for compatibility
implementation 'me.leolin:ShortcutBadger:1.1.22@aar'

View file

@ -134,6 +134,11 @@
<activity android:name=".features.qrcode.QrCodeScannerActivity" />
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" />
<activity android:name=".features.attachments.preview.AttachmentsPreviewActivity" />
<!-- Services -->
<service

View file

@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentFactory
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewFragment
import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
@ -348,4 +349,10 @@ interface FragmentModule {
@IntoMap
@FragmentKey(CrossSigningSettingsFragment::class)
fun bindCrossSigningSettingsFragment(fragment: CrossSigningSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(AttachmentsPreviewFragment::class)
fun bindAttachmentsPreviewFragment(fragment: AttachmentsPreviewFragment): Fragment
}

View file

@ -0,0 +1,30 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import com.airbnb.epoxy.TypedEpoxyController
import javax.inject.Inject
class AttachmentPreviewController @Inject constructor() : TypedEpoxyController<AttachmentsPreviewViewState>() {
override fun buildModels(data: AttachmentsPreviewViewState) {
data.attachments.forEach {
}
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import android.view.View
import android.widget.ImageView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import kotlinx.android.synthetic.main.item_attachment_preview.view.*
@EpoxyModelClass(layout = R.layout.item_attachment_preview)
abstract class AttachmentPreviewItem : VectorEpoxyModel<AttachmentPreviewItem.Holder>() {
@EpoxyAttribute lateinit var attachment: ContentAttachmentData
@EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
holder.view.setOnClickListener(clickListener)
// If name is empty, use userId as name and force it being centered
val mimeType = attachment.mimeType
val path = attachment.path
if (mimeType != null && (mimeType.startsWith("image") || mimeType.startsWith("video"))) {
Glide.with(holder.view.context)
.asBitmap()
.load(path)
.apply(RequestOptions().frame(0))
.into(holder.imageView)
} else {
holder.imageView.attachmentPreviewImageView.setImageResource(R.drawable.filetype_attachment)
holder.imageView.attachmentPreviewImageView.scaleType = ImageView.ScaleType.FIT_CENTER
}
}
class Holder : VectorEpoxyHolder() {
val imageView by bind<ImageView>(R.id.attachmentPreviewImageView)
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class AttachmentsPreviewAction : VectorViewModelAction

View file

@ -0,0 +1,54 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import android.content.Context
import android.content.Intent
import androidx.appcompat.widget.Toolbar
import im.vector.riotx.R
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
companion object {
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent {
return Intent(context, AttachmentsPreviewArgs::class.java).apply {
putExtra(EXTRA_FRAGMENT_ARGS, args)
}
}
}
override fun getLayoutRes() = R.layout.activity_simple
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)
?: return
addFragment(R.id.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs)
}
}
override fun configure(toolbar: Toolbar) {
configureToolbar(toolbar)
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_attachments_preview.*
import javax.inject.Inject
@Parcelize
data class AttachmentsPreviewArgs(
val attachments: List<ContentAttachmentData>
) : Parcelable
class AttachmentsPreviewFragment @Inject constructor(
val viewModelFactory: AttachmentsPreviewViewModel.Factory,
private val attachmentPreviewController: AttachmentPreviewController
) : VectorBaseFragment() {
private val fragmentArgs: AttachmentsPreviewArgs by args()
private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_attachments_preview
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
}
override fun onDestroyView() {
super.onDestroyView()
attachmentPreviewerList.cleanup()
}
private fun setupRecyclerView() {
attachmentPreviewerList.configureWith(attachmentPreviewController, hasFixedSize = true)
}
override fun invalidate() = withState(viewModel) { state ->
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import im.vector.riotx.core.platform.VectorViewEvents
sealed class AttachmentsPreviewViewEvents : VectorViewEvents

View file

@ -0,0 +1,47 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.riotx.core.platform.VectorViewModel
class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState)
: VectorViewModel<AttachmentsPreviewViewState, AttachmentsPreviewAction, AttachmentsPreviewViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: AttachmentsPreviewViewState): AttachmentsPreviewViewModel
}
companion object : MvRxViewModelFactory<AttachmentsPreviewViewModel, AttachmentsPreviewViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: AttachmentsPreviewViewState): AttachmentsPreviewViewModel? {
val fragment: AttachmentsPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state)
}
}
override fun handle(action: AttachmentsPreviewAction) {
//TODO
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package im.vector.riotx.features.attachments.preview
import com.airbnb.mvrx.MvRxState
import im.vector.matrix.android.api.session.content.ContentAttachmentData
data class AttachmentsPreviewViewState(
val attachments: List<ContentAttachmentData>
) : MvRxState {
constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments)
}

View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/VectorToolbarStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/attachmentPreviewerContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/attachmentPreviewerFileName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<ImageView
android:id="@+id/attachmentPreviewerContainerImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside" />
<VideoView
android:id="@+id/attachmentPreviewerVideoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<ImageView
android:id="@+id/attachmentPreviewerThumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<ImageView
android:id="@+id/attachmentPreviewerVideoPlay"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:src="@drawable/ic_material_play_circle" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/attachmentPreviewerList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/attachmentPreviewerFileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@id/attachmentPreviewerList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Filename" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/attachmentPreviewerSendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_material_send_white"
app:layout_constraintBottom_toTopOf="@+id/attachmentPreviewerFileName"
app:layout_constraintEnd_toEndOf="@id/attachmentPreviewerContainer" />
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dip"
card_view:cardBackgroundColor="@android:color/transparent"
card_view:cardElevation="0dp">
<ImageView
android:id="@+id/attachmentPreviewImageView"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
tools:src="@tools:sample/backgrounds/scenic" />
</android.support.v7.widget.CardView>