Attachment: continue working on preview screen

This commit is contained in:
Ganard 2020-02-06 18:42:18 +01:00 committed by Benoit Marty
parent 6471787232
commit b7a7aa2f15
17 changed files with 385 additions and 46 deletions

View file

@ -29,6 +29,7 @@ data class ContentAttachmentData(
val width: Long? = 0,
val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
val name: String? = null,
val queryUri: String,
val path: String,
val mimeType: String?,
val type: Type

View file

@ -143,7 +143,10 @@
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" />
<activity android:name=".features.attachments.preview.AttachmentsPreviewActivity" />
<activity
android:name=".features.attachments.preview.AttachmentsPreviewActivity"
android:theme="@style/AppTheme.AttachmentsPreview" />
<!-- Services -->

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 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.core.utils
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SnapHelper
interface OnSnapPositionChangeListener {
fun onSnapPositionChange(position: Int)
}
fun RecyclerView.attachSnapHelperWithListener(
snapHelper: SnapHelper,
behavior: SnapOnScrollListener.Behavior = SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE,
onSnapPositionChangeListener: OnSnapPositionChangeListener) {
snapHelper.attachToRecyclerView(this)
val snapOnScrollListener = SnapOnScrollListener(snapHelper, behavior, onSnapPositionChangeListener)
addOnScrollListener(snapOnScrollListener)
}
fun SnapHelper.getSnapPosition(recyclerView: RecyclerView): Int {
val layoutManager = recyclerView.layoutManager ?: return RecyclerView.NO_POSITION
val snapView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION
return layoutManager.getPosition(snapView)
}
class SnapOnScrollListener(
private val snapHelper: SnapHelper,
var behavior: Behavior = Behavior.NOTIFY_ON_SCROLL,
var onSnapPositionChangeListener: OnSnapPositionChangeListener? = null
) : RecyclerView.OnScrollListener() {
enum class Behavior {
NOTIFY_ON_SCROLL,
NOTIFY_ON_SCROLL_STATE_IDLE
}
private var snapPosition = RecyclerView.NO_POSITION
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (behavior == Behavior.NOTIFY_ON_SCROLL) {
maybeNotifySnapPositionChange(recyclerView)
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE
&& newState == RecyclerView.SCROLL_STATE_IDLE) {
maybeNotifySnapPositionChange(recyclerView)
}
}
private fun maybeNotifySnapPositionChange(recyclerView: RecyclerView) {
val snapPosition = snapHelper.getSnapPosition(recyclerView)
val snapPositionChanged = this.snapPosition != snapPosition
if (snapPositionChanged) {
onSnapPositionChangeListener?.onSnapPositionChange(snapPosition)
this.snapPosition = snapPosition
}
}
}

View file

@ -37,7 +37,8 @@ fun ChosenFile.toContentAttachmentData(): ContentAttachmentData {
type = mapType(),
size = size,
date = createdAt?.time ?: System.currentTimeMillis(),
name = displayName
name = displayName,
queryUri = queryUri
)
}
@ -50,7 +51,8 @@ fun ChosenAudio.toContentAttachmentData(): ContentAttachmentData {
size = size,
date = createdAt?.time ?: System.currentTimeMillis(),
name = displayName,
duration = duration
duration = duration,
queryUri = queryUri
)
}
@ -74,7 +76,8 @@ fun ChosenImage.toContentAttachmentData(): ContentAttachmentData {
height = height.toLong(),
width = width.toLong(),
exifOrientation = orientation,
date = createdAt?.time ?: System.currentTimeMillis()
date = createdAt?.time ?: System.currentTimeMillis(),
queryUri = queryUri
)
}
@ -89,6 +92,7 @@ fun ChosenVideo.toContentAttachmentData(): ContentAttachmentData {
height = height.toLong(),
width = width.toLong(),
duration = duration,
name = displayName
name = displayName,
queryUri = queryUri
)
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 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
import im.vector.matrix.android.api.session.content.ContentAttachmentData
fun ContentAttachmentData.isPreviewable(): Boolean {
return type == ContentAttachmentData.Type.IMAGE || type == ContentAttachmentData.Type.VIDEO
}
fun List<ContentAttachmentData>.filterPreviewables(): List<ContentAttachmentData> {
return filter { it.isPreviewable() }
}
fun List<ContentAttachmentData>.filterNonPreviewables(): List<ContentAttachmentData> {
return filter { it.isPreviewable().not() }
}

View file

@ -35,18 +35,18 @@ class AttachmentBigPreviewController @Inject constructor() : TypedEpoxyControlle
class AttachmentMiniaturePreviewController @Inject constructor() : TypedEpoxyController<AttachmentsPreviewViewState>() {
interface Callback {
fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData)
fun onAttachmentClicked(position: Int, contentAttachmentData: ContentAttachmentData)
}
var callback: Callback? = null
override fun buildModels(data: AttachmentsPreviewViewState) {
data.attachments.forEach {
data.attachments.forEachIndexed { index, contentAttachmentData ->
attachmentMiniaturePreviewItem {
id(it.path)
attachment(it)
id(contentAttachmentData.path)
attachment(contentAttachmentData)
clickListener { _ ->
callback?.onAttachmentClicked(it)
callback?.onAttachmentClicked(index, contentAttachmentData)
}
}
}

View file

@ -19,4 +19,10 @@ package im.vector.riotx.features.attachments.preview
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class AttachmentsPreviewAction : VectorViewModelAction
sealed class AttachmentsPreviewAction : VectorViewModelAction {
object RemoveCurrentAttachment : AttachmentsPreviewAction()
data class SetCurrentAttachment(val index: Int): AttachmentsPreviewAction()
data class UpdatePathOfCurrentAttachment(val newPath: String): AttachmentsPreviewAction()
}

View file

@ -19,19 +19,20 @@ package im.vector.riotx.features.attachments.preview
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.widget.Toolbar
import im.vector.matrix.android.api.session.content.ContentAttachmentData
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
import im.vector.riotx.features.themes.ActivityOtherThemes
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
companion object {
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
const val RESULT_NAME = "ATTACHMENTS_PREVIEW_RESULT"
const val REQUEST_CODE = 55
fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent {
@ -39,15 +40,16 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
putExtra(EXTRA_FRAGMENT_ARGS, args)
}
}
fun getOutput(intent: Intent): List<ContentAttachmentData> {
return intent.getParcelableArrayListExtra(RESULT_NAME)
}
}
override fun getOtherThemes() = ActivityOtherThemes.AttachmentsPreview
override fun getLayoutRes() = R.layout.activity_simple
override fun onCreate(savedInstanceState: Bundle?) {
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
super.onCreate(savedInstanceState)
}
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)

View file

@ -17,24 +17,41 @@
package im.vector.riotx.features.attachments.preview
import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.yalantis.ucrop.UCrop
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.platform.VectorBaseFragment
import im.vector.riotx.core.utils.OnSnapPositionChangeListener
import im.vector.riotx.core.utils.SnapOnScrollListener
import im.vector.riotx.core.utils.attachSnapHelperWithListener
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_attachments_preview.*
import timber.log.Timber
import java.io.File
import javax.inject.Inject
@Parcelize
data class AttachmentsPreviewArgs(
val roomId: String,
val attachments: List<ContentAttachmentData>
) : Parcelable
@ -51,13 +68,107 @@ class AttachmentsPreviewFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
applyInsets()
setupRecyclerViews()
setupToolbar(attachmentPreviewerToolbar)
attachmentPreviewerSendButton.setOnClickListener {
setResultAndFinish()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == RESULT_OK) {
if (requestCode == UCrop.REQUEST_CROP && data != null) {
Timber.v("Crop success")
handleCropResult(data)
}
}
if (resultCode == UCrop.RESULT_ERROR) {
Timber.v("Crop error")
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.attachmentsPreviewRemoveAction -> {
handleRemoveAction()
true
}
R.id.attachmentsPreviewEditAction -> {
handleEditAction()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}
override fun getMenuRes() = R.menu.vector_attachments_preview
override fun onDestroyView() {
super.onDestroyView()
attachmentPreviewerMiniatureList.cleanup()
attachmentPreviewerBigList.cleanup()
}
override fun invalidate() = withState(viewModel) { state ->
if (state.attachments.isEmpty()) {
requireActivity().setResult(RESULT_CANCELED)
requireActivity().finish()
} else {
attachmentMiniaturePreviewController.setData(state)
attachmentBigPreviewController.setData(state)
attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex)
attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex)
}
}
override fun onAttachmentClicked(position: Int, contentAttachmentData: ContentAttachmentData) {
viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position))
}
private fun setResultAndFinish() = withState(viewModel) {
val resultIntent = Intent().apply {
putParcelableArrayListExtra(AttachmentsPreviewActivity.RESULT_NAME, ArrayList(it.attachments))
}
requireActivity().setResult(RESULT_OK, resultIntent)
requireActivity().finish()
}
private fun applyInsets() {
view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerBottomContainer) { v, insets ->
v.updatePadding(bottom = insets.systemWindowInsetBottom)
insets
}
ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerToolbar) { v, insets ->
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.systemWindowInsetTop
}
insets
}
}
private fun handleCropResult(result: Intent) {
val resultPath = UCrop.getOutput(result)?.path
if (resultPath != null) {
viewModel.handle(AttachmentsPreviewAction.UpdatePathOfCurrentAttachment(resultPath))
} else {
Toast.makeText(requireContext(), "Cannot retrieve cropped value", Toast.LENGTH_SHORT).show()
}
}
private fun handleRemoveAction() {
viewModel.handle(AttachmentsPreviewAction.RemoveCurrentAttachment)
}
private fun handleEditAction() = withState(viewModel) {
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
val destinationFile = File(requireContext().cacheDir, "${currentAttachment.name}_edited_image_${System.currentTimeMillis()}")
UCrop.of(currentAttachment.queryUri.toUri(), destinationFile.toUri())
.withOptions(UCrop.Options())
.start(requireContext(), this)
}
private fun setupRecyclerViews() {
@ -67,20 +178,12 @@ class AttachmentsPreviewFragment @Inject constructor(
attachmentMiniaturePreviewController.callback = this
attachmentPreviewerBigList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(attachmentPreviewerBigList)
attachmentPreviewerBigList.attachSnapHelperWithListener(PagerSnapHelper(), SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE, object : OnSnapPositionChangeListener {
override fun onSnapPositionChange(position: Int) {
viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position))
}
})
attachmentPreviewerBigList.setHasFixedSize(true)
attachmentPreviewerBigList.adapter = attachmentBigPreviewController.adapter
}
override fun invalidate() = withState(viewModel) { state ->
attachmentMiniaturePreviewController.setData(state)
attachmentBigPreviewController.setData(state)
}
override fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData) {
}
}

View file

@ -17,12 +17,15 @@
package im.vector.riotx.features.attachments.preview
import androidx.lifecycle.viewModelScope
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.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import kotlinx.coroutines.launch
class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState)
: VectorViewModel<AttachmentsPreviewViewState, AttachmentsPreviewAction, AttachmentsPreviewViewEvents>(initialState) {
@ -42,6 +45,36 @@ class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialS
}
override fun handle(action: AttachmentsPreviewAction) {
//TODO
when (action) {
is AttachmentsPreviewAction.SetCurrentAttachment -> handleSetCurrentAttachment(action)
is AttachmentsPreviewAction.UpdatePathOfCurrentAttachment -> handleUpdatePathOfCurrentAttachment(action)
AttachmentsPreviewAction.RemoveCurrentAttachment -> handleRemoveCurrentAttachment()
}.exhaustive
}
private fun handleRemoveCurrentAttachment() = withState {
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
val attachments = it.attachments.minusElement(currentAttachment)
val newAttachmentIndex = it.currentAttachmentIndex.coerceAtMost(attachments.size - 1)
setState {
copy(attachments = attachments, currentAttachmentIndex = newAttachmentIndex)
}
}
private fun handleUpdatePathOfCurrentAttachment(action: AttachmentsPreviewAction.UpdatePathOfCurrentAttachment) = withState {
val attachments = it.attachments.mapIndexed { index, contentAttachmentData ->
if (index == it.currentAttachmentIndex) {
contentAttachmentData.copy(path = action.newPath)
} else {
contentAttachmentData
}
}
setState {
copy(attachments = attachments)
}
}
private fun handleSetCurrentAttachment(action: AttachmentsPreviewAction.SetCurrentAttachment) = setState {
copy(currentAttachmentIndex = action.index)
}
}

View file

@ -21,7 +21,8 @@ import com.airbnb.mvrx.MvRxState
import im.vector.matrix.android.api.session.content.ContentAttachmentData
data class AttachmentsPreviewViewState(
val attachments: List<ContentAttachmentData>
val attachments: List<ContentAttachmentData>,
val currentAttachmentIndex: Int = 0
) : MvRxState {
constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments)

View file

@ -116,6 +116,8 @@ import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.attachments.AttachmentTypeSelectorView
import im.vector.riotx.features.attachments.AttachmentsHelper
import im.vector.riotx.features.attachments.ContactAttachment
import im.vector.riotx.features.attachments.filterNonPreviewables
import im.vector.riotx.features.attachments.filterPreviewables
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewActivity
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs
import im.vector.riotx.features.command.Command
@ -502,6 +504,10 @@ class RoomDetailFragment @Inject constructor(
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
when (requestCode) {
AttachmentsPreviewActivity.REQUEST_CODE -> {
val sendData = AttachmentsPreviewActivity.getOutput(data)
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData))
}
REACTION_SELECT_REQUEST_CODE -> {
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction))
@ -1342,9 +1348,16 @@ class RoomDetailFragment @Inject constructor(
// AttachmentsHelper.Callback
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(attachments))
val previewable = attachments.filterPreviewables()
val nonPreviewable = attachments.filterNonPreviewables()
if (nonPreviewable.isNotEmpty()) {
roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable))
}
if (previewable.isNotEmpty()) {
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(roomDetailArgs.roomId, previewable))
startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
}
}
override fun onAttachmentsProcessFailed() {
Toast.makeText(requireContext(), R.string.error_attachment, Toast.LENGTH_SHORT).show()

View file

@ -32,4 +32,10 @@ sealed class ActivityOtherThemes(@StyleRes val dark: Int,
R.style.AppTheme_Black,
R.style.AppTheme_Status
)
object AttachmentsPreview : ActivityOtherThemes(
R.style.AppTheme_AttachmentsPreview,
R.style.AppTheme_AttachmentsPreview,
R.style.AppTheme_AttachmentsPreview
)
}

View file

@ -1,37 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
xmlns:tools="http://schemas.android.com/tools">
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/attachmentPreviewerBigList"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_attachment_big_preview"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/item_attachment_big_preview" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/attachmentPreviewerToolbar"
style="@style/VectorToolbarStyle"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/attachmentPreviewerBottomContainer"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#80000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/attachmentPreviewerMiniatureList"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/attachmentPreviewerSendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_send"
app:layout_constraintTop_toTopOf="@id/attachmentPreviewerBottomContainer"
app:layout_constraintBottom_toTopOf="@id/attachmentPreviewerBottomContainer"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu 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"
tools:context=".features.attachments.preview.AttachmentsPreviewActivity">
<item
android:id="@+id/attachmentsPreviewRemoveAction"
android:icon="@drawable/ic_delete"
android:title="Delete"
app:showAsAction="always" />
<item
android:id="@+id/attachmentsPreviewEditAction"
android:icon="@drawable/ic_edit"
android:title="Edit"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Launcher Theme, only used for VectorLauncherActivity (will be use even before the Activity is started) -->
<style name="AppTheme.Launcher" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:windowBackground">@drawable/splash</item>
<item name="colorPrimaryDark">@color/primary_color_dark</item>
</style>
<style name="AppTheme.AttachmentsPreview" parent="AppTheme.Base.Black">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>

View file

@ -8,4 +8,6 @@
<item name="colorPrimaryDark">@color/primary_color_dark</item>
</style>
<style name="AppTheme.AttachmentsPreview" parent="AppTheme.Base.Black"/>
</resources>