Center selected item in media rail.

Fixes #12582
main
Nicholas 2022-11-14 17:28:36 -05:00 zatwierdzone przez Alex Hart
rodzic 52a5fb8ea2
commit fb8e81cf50
11 zmienionych plików z 248 dodań i 374 usunięć

Wyświetl plik

@ -513,7 +513,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
mediaIds = state.sharedMediaIds,
onMediaRecordClick = { mediaRecord, isLtr ->
startActivityForResult(
MediaIntentFactory.intentFromMediaRecord(requireContext(), mediaRecord, isLtr),
MediaIntentFactory.intentFromMediaRecord(requireContext(), mediaRecord, isLtr, allMediaInRail = true),
REQUEST_CODE_RETURN_FROM_MEDIA
)
}

Wyświetl plik

@ -52,7 +52,8 @@ object MediaIntentFactory {
fun intentFromMediaRecord(
context: Context,
mediaRecord: MediaRecord,
leftIsRecent: Boolean
leftIsRecent: Boolean,
allMediaInRail: Boolean
): Intent {
val attachment: DatabaseAttachment = mediaRecord.attachment!!
return create(
@ -65,7 +66,7 @@ object MediaIntentFactory {
attachment.size,
attachment.caption,
leftIsRecent,
allMediaInRail = true,
allMediaInRail = allMediaInRail,
sorting = MediaDatabase.Sorting.Newest,
isVideoGif = attachment.isVideoGif
)

Wyświetl plik

@ -4,6 +4,7 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.util.MediaUtil
class MediaPreviewV2Adapter(val fragment: Fragment) : FragmentStateAdapter(fragment) {
@ -37,6 +38,10 @@ class MediaPreviewV2Adapter(val fragment: Fragment) : FragmentStateAdapter(fragm
return fragment
}
fun findItemPosition(media: Media): Int {
return items.indexOfFirst { it.uri == media.uri }
}
fun updateBackingItems(newItems: Collection<Attachment>) {
if (newItems != items) {
items = newItems.toList()

Wyświetl plik

@ -24,7 +24,8 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.MarginPageTransformer
import androidx.viewpager2.widget.ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT
@ -42,7 +43,9 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.databinding.FragmentMediaPreviewV2Binding
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter.ImageLoadingListener
import org.thoughtcrime.securesms.mediapreview.mediarail.CenterDecoration
import org.thoughtcrime.securesms.mediapreview.mediarail.MediaRailAdapter
import org.thoughtcrime.securesms.mediapreview.mediarail.MediaRailAdapter.ImageLoadingListener
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
import org.thoughtcrime.securesms.mms.GlideApp
@ -137,20 +140,11 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
private fun initializeAlbumRail() {
binding.mediaPreviewPlaybackControls.recyclerView.apply {
this.itemAnimator = null // Or can crash when set to INVISIBLE while animating by FullscreenHelper https://issuetracker.google.com/issues/148720682
PagerSnapHelper().attachToRecyclerView(this)
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
addItemDecoration(CenterDecoration(0))
albumRailAdapter = MediaRailAdapter(
GlideApp.with(this@MediaPreviewV2Fragment),
object : MediaRailAdapter.RailItemListener {
override fun onRailItemClicked(distanceFromActive: Int) {
binding.mediaPager.currentItem += distanceFromActive
}
override fun onRailItemDeleteClicked(distanceFromActive: Int) {
throw UnsupportedOperationException("Callback unsupported.")
}
},
false,
{ media -> jumpViewPagerToMedia(media) },
object : ImageLoadingListener() {
override fun onAllRequestsFinished() {
crossfadeViewIn(this@apply)
@ -281,14 +275,40 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
if (albumRail.visibility == GONE) {
albumRail.visibility = View.INVISIBLE
}
albumRailAdapter.setMedia(albumThumbnailMedia, albumPosition)
albumRail.smoothScrollToPosition(albumPosition)
albumRailAdapter.currentItemPosition = albumPosition
albumRailAdapter.submitList(albumThumbnailMedia)
scrollAlbumRailToCurrentAdapterPosition()
} else {
albumRail.visibility = View.GONE
albumRailAdapter.setMedia(emptyList())
albumRailAdapter.submitList(emptyList())
albumRailAdapter.imageLoadingListener.reset()
}
}
private fun scrollAlbumRailToCurrentAdapterPosition() {
val currentItemPosition = albumRailAdapter.currentItemPosition
val currentList = albumRailAdapter.currentList
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
var selectedItemWidth = -1
for (i in currentList.indices) {
val isSelected = i == currentItemPosition
val stableId = albumRailAdapter.getItemId(i)
val viewHolder = albumRail.findViewHolderForItemId(stableId) as? MediaRailAdapter.MediaRailViewHolder
if (viewHolder != null) {
viewHolder.setSelectedItem(isSelected)
if (isSelected) {
selectedItemWidth = viewHolder.itemView.width
}
}
}
val offsetFromStart = (albumRail.width - selectedItemWidth) / 2
val smoothScroller = OffsetSmoothScroller(requireContext(), offsetFromStart)
smoothScroller.targetPosition = currentItemPosition
val layoutManager = albumRail.layoutManager as LinearLayoutManager
layoutManager.startSmoothScroll(smoothScroller)
}
private fun crossfadeViewIn(view: View, duration: Long = 200) {
if (!view.isVisible) {
val viewPropertyAnimator = view.animate()
@ -297,6 +317,11 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
.withStartAction {
view.visibility = VISIBLE
}
.withEndAction {
if (view == binding.mediaPreviewPlaybackControls.recyclerView) {
scrollAlbumRailToCurrentAdapterPosition()
}
}
if (Build.VERSION.SDK_INT >= 21) {
viewPropertyAnimator.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
}
@ -306,6 +331,12 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
private fun getMediaPreviewFragmentFromChildFragmentManager(currentPosition: Int) = childFragmentManager.findFragmentByTag("f$currentPosition") as? MediaPreviewFragment
private fun jumpViewPagerToMedia(media: Media) {
val viewPagerAdapter = binding.mediaPager.adapter as MediaPreviewV2Adapter
val position = viewPagerAdapter.findItemPosition(media)
binding.mediaPager.setCurrentItem(position, true)
}
private fun getTitleText(mediaRecord: MediaDatabase.MediaRecord, showThread: Boolean): String {
val recipient: Recipient = Recipient.live(mediaRecord.recipientId).get()
val defaultFromString: String = if (mediaRecord.isOutgoing) {
@ -482,6 +513,16 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
viewModel.onDestroyView()
}
private class OffsetSmoothScroller(context: Context, val offset: Int) : LinearSmoothScroller(context) {
override fun getHorizontalSnapPreference(): Int {
return SNAP_TO_START
}
override fun calculateDxToMakeVisible(view: View?, snapPreference: Int): Int {
return offset + super.calculateDxToMakeVisible(view, snapPreference)
}
}
companion object {
const val ARGS_KEY: String = "args"

Wyświetl plik

@ -1,249 +0,0 @@
package org.thoughtcrime.securesms.mediapreview;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.MediaRailViewHolder> {
private static final int TYPE_MEDIA = 1;
private static final int TYPE_BUTTON = 2;
private final GlideRequests glideRequests;
private final List<Media> media;
private final RailItemListener listener;
private final StableIdGenerator<Media> stableIdGenerator;
private final ImageLoadingListener imageLoadingListener;
private RailItemAddListener addListener;
private int activePosition;
private boolean editable;
private boolean interactive;
public MediaRailAdapter(@NonNull GlideRequests glideRequests, @NonNull RailItemListener listener, boolean editable, ImageLoadingListener imageLoadingListener) {
this.glideRequests = glideRequests;
this.media = new ArrayList<>();
this.listener = listener;
this.editable = editable;
this.stableIdGenerator = new StableIdGenerator<>();
this.interactive = true;
this.imageLoadingListener = imageLoadingListener;
setHasStableIds(true);
}
@NonNull
@Override
public MediaRailViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
switch (type) {
case TYPE_MEDIA:
return new MediaViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.mediarail_media_item, viewGroup, false));
case TYPE_BUTTON:
return new ButtonViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.mediarail_button_item, viewGroup, false));
default:
throw new UnsupportedOperationException("Unsupported view type: " + type);
}
}
@Override
public void onBindViewHolder(@NonNull MediaRailViewHolder viewHolder, int i) {
switch (getItemViewType(i)) {
case TYPE_MEDIA:
((MediaViewHolder) viewHolder).bind(media.get(i), i == activePosition, glideRequests, listener, i - activePosition, editable, interactive, imageLoadingListener);
break;
case TYPE_BUTTON:
((ButtonViewHolder) viewHolder).bind(addListener);
break;
default:
throw new UnsupportedOperationException("Unsupported view type: " + getItemViewType(i));
}
}
@Override
public int getItemViewType(int position) {
if (editable && position == getItemCount() - 1) {
return TYPE_BUTTON;
} else {
return TYPE_MEDIA;
}
}
@Override
public void onViewRecycled(@NonNull MediaRailViewHolder holder) {
holder.recycle();
}
@Override
public int getItemCount() {
return editable ? media.size() + 1 : media.size();
}
@Override
public long getItemId(int position) {
switch (getItemViewType(position)) {
case TYPE_MEDIA:
return stableIdGenerator.getId(media.get(position));
case TYPE_BUTTON:
return Long.MAX_VALUE;
default:
throw new UnsupportedOperationException("Unsupported view type: " + getItemViewType(position));
}
}
public void setMedia(@NonNull List<Media> media) {
setMedia(media, activePosition);
}
public void setMedia(@NonNull List<Media> records, int activePosition) {
this.activePosition = activePosition;
this.media.clear();
this.media.addAll(records);
notifyDataSetChanged();
}
public void setActivePosition(int activePosition) {
this.activePosition = activePosition;
notifyDataSetChanged();
}
public void setAddButtonListener(@Nullable RailItemAddListener addListener) {
this.addListener = addListener;
notifyDataSetChanged();
}
public void setEditable(boolean editable) {
this.editable = editable;
notifyDataSetChanged();
}
public void setInteractive(boolean interactive) {
this.interactive = interactive;
notifyDataSetChanged();
}
static abstract class MediaRailViewHolder extends RecyclerView.ViewHolder {
public MediaRailViewHolder(@NonNull View itemView) {
super(itemView);
}
abstract void recycle();
}
static class MediaViewHolder extends MediaRailViewHolder {
private final ThumbnailView image;
private final View outline;
private final View deleteButton;
private final View captionIndicator;
MediaViewHolder(@NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.rail_item_image);
outline = itemView.findViewById(R.id.rail_item_outline);
deleteButton = itemView.findViewById(R.id.rail_item_delete);
captionIndicator = itemView.findViewById(R.id.rail_item_caption);
}
void bind(@NonNull Media media, boolean isActive, @NonNull GlideRequests glideRequests,
@NonNull RailItemListener railItemListener, int distanceFromActive, boolean editable,
boolean interactive, @NonNull ImageLoadingListener listener)
{
listener.onRequest();
image.setImageResource(glideRequests, media.getUri(), 0, 0, false, listener);
image.setOnClickListener(v -> railItemListener.onRailItemClicked(distanceFromActive));
outline.setVisibility(isActive && interactive ? View.VISIBLE : View.GONE);
captionIndicator.setVisibility(media.getCaption().isPresent() ? View.VISIBLE : View.GONE);
if (editable && isActive && interactive) {
deleteButton.setVisibility(View.VISIBLE);
deleteButton.setOnClickListener(v -> railItemListener.onRailItemDeleteClicked(distanceFromActive));
} else {
deleteButton.setVisibility(View.GONE);
}
}
void recycle() {
image.setOnClickListener(null);
deleteButton.setOnClickListener(null);
}
}
static class ButtonViewHolder extends MediaRailViewHolder {
public ButtonViewHolder(@NonNull View itemView) {
super(itemView);
}
void bind(@Nullable RailItemAddListener addListener) {
if (addListener != null) {
itemView.setOnClickListener(v -> addListener.onRailItemAddClicked());
}
}
@Override
void recycle() {
itemView.setOnClickListener(null);
}
}
public interface RailItemListener {
void onRailItemClicked(int distanceFromActive);
void onRailItemDeleteClicked(int distanceFromActive);
}
public interface RailItemAddListener {
void onRailItemAddClicked();
}
abstract static class ImageLoadingListener implements RequestListener<Drawable> {
final private AtomicInteger activeJobs = new AtomicInteger();
void onRequest() {
activeJobs.incrementAndGet();
}
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
int count = activeJobs.decrementAndGet();
if (count == 0) {
onAllRequestsFinished();
}
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
int count = activeJobs.decrementAndGet();
if (count == 0) {
onAllRequestsFinished();
}
return false;
}
abstract void onAllRequestsFinished();
}
}

Wyświetl plik

@ -0,0 +1,45 @@
package org.thoughtcrime.securesms.mediapreview.mediarail
import android.graphics.Rect
import android.view.View
import androidx.annotation.Px
import androidx.core.view.doOnPreDraw
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
/**
* From: <a href="https://stackoverflow.com/a/53510142">https://stackoverflow.com/a/53510142</a>
*/
class CenterDecoration(@Px private val spacing: Int) : RecyclerView.ItemDecoration() {
private var firstViewWidth = -1
private var lastViewWidth = -1
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
val adapterPosition = (view.layoutParams as RecyclerView.LayoutParams).absoluteAdapterPosition
val layoutManager = parent.layoutManager as LinearLayoutManager
if (adapterPosition == 0) {
if (view.width != firstViewWidth) {
view.doOnPreDraw { parent.invalidateItemDecorations() }
}
firstViewWidth = view.width
outRect.left = parent.width / 2 - view.width / 2
if (layoutManager.itemCount > 1) {
outRect.right = spacing / 2
} else {
outRect.right = outRect.left
}
} else if (adapterPosition == layoutManager.itemCount - 1) {
if (view.width != lastViewWidth) {
view.doOnPreDraw { parent.invalidateItemDecorations() }
}
lastViewWidth = view.width
outRect.right = parent.width / 2 - view.width / 2
outRect.left = spacing / 2
} else {
outRect.left = spacing / 2
outRect.right = spacing / 2
}
}
}

Wyświetl plik

@ -0,0 +1,132 @@
package org.thoughtcrime.securesms.mediapreview.mediarail
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.ThumbnailView
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator
import org.thoughtcrime.securesms.util.visible
import java.util.concurrent.atomic.AtomicInteger
/**
* This is the RecyclerView.Adapter for the row of thumbnails present in the media viewer screen.
*/
class MediaRailAdapter(private val glideRequests: GlideRequests, listener: RailItemListener, imageLoadingListener: ImageLoadingListener) : ListAdapter<Media, MediaRailAdapter.MediaRailViewHolder>(MediaDiffer()) {
val imageLoadingListener: ImageLoadingListener
var currentItemPosition: Int = -1
private val listener: RailItemListener
private val stableIdGenerator: StableIdGenerator<Media>
init {
this.listener = listener
stableIdGenerator = StableIdGenerator()
this.imageLoadingListener = imageLoadingListener
setHasStableIds(true)
}
override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int): MediaRailViewHolder {
return MediaRailViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.mediarail_media_item, viewGroup, false))
}
override fun onBindViewHolder(viewHolder: MediaRailViewHolder, i: Int) {
viewHolder.bind(getItem(i), i == currentItemPosition, glideRequests, listener, imageLoadingListener)
}
override fun onViewRecycled(holder: MediaRailViewHolder) {
holder.recycle()
}
override fun getItemId(position: Int): Long {
return stableIdGenerator.getId(getItem(position))
}
class MediaRailViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val image: ThumbnailView
private val outline: View
private val captionIndicator: View
init {
image = itemView.findViewById(R.id.rail_item_image)
outline = itemView.findViewById(R.id.rail_item_outline)
captionIndicator = itemView.findViewById(R.id.rail_item_caption)
}
fun bind(
media: Media,
isCurrentlySelected: Boolean,
glideRequests: GlideRequests,
railItemListener: RailItemListener,
listener: ImageLoadingListener
) {
listener.onRequest()
image.setImageResource(glideRequests, media.uri, 0, 0, false, listener)
image.setOnClickListener { railItemListener.onRailItemClicked(media) }
captionIndicator.visibility = if (media.caption.isPresent) View.VISIBLE else View.GONE
setSelectedItem(isCurrentlySelected)
}
fun recycle() {
image.setOnClickListener(null)
}
fun setSelectedItem(isActive: Boolean) {
outline.visible = isActive
}
}
fun interface RailItemListener {
fun onRailItemClicked(media: Media)
}
abstract class ImageLoadingListener : RequestListener<Drawable?> {
private val activeJobs = AtomicInteger()
fun onRequest() {
activeJobs.incrementAndGet()
}
final override fun onLoadFailed(e: GlideException?, model: Any, target: Target<Drawable?>, isFirstResource: Boolean): Boolean {
val count = activeJobs.decrementAndGet()
if (count == 0) {
onAllRequestsFinished()
}
return false
}
final override fun onResourceReady(resource: Drawable?, model: Any, target: Target<Drawable?>, dataSource: DataSource, isFirstResource: Boolean): Boolean {
val count = activeJobs.decrementAndGet()
if (count == 0) {
onAllRequestsFinished()
}
return false
}
fun reset() {
activeJobs.set(0)
}
abstract fun onAllRequestsFinished()
}
class MediaDiffer : DiffUtil.ItemCallback<Media>() {
override fun areItemsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem.uri == newItem.uri
}
override fun areContentsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem == newItem
}
}
}

Wyświetl plik

@ -50,7 +50,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/media_preview_album_rail"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="11dp"
@ -58,7 +58,6 @@
android:layout_marginEnd="12dp"
android:orientation="horizontal"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:layout_height="64dp" />
<LinearLayout

Wyświetl plik

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/core_grey_95">
<org.thoughtcrime.securesms.components.viewpager.HackyViewPager
android:id="@+id/media_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"/>
<LinearLayout
android:id="@+id/media_preview_details_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:animateLayoutChanges="true"
android:background="@drawable/image_preview_shade"
android:gravity="bottom"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<org.thoughtcrime.securesms.components.MaxHeightScrollView
android:id="@+id/media_preview_caption_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="32dp"
android:animateLayoutChanges="true"
app:scrollView_maxHeight="120dp">
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/media_preview_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
style="@style/Signal.Text.Body"
android:textColor="@color/core_white"
android:gravity="bottom"
tools:text="With great power comes great responsibility." />
</org.thoughtcrime.securesms.components.MaxHeightScrollView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/media_preview_album_rail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
tools:layout_height="64dp"/>
<FrameLayout
android:id="@+id/media_preview_playback_controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"/>
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/media_preview_bar_background"
app:elevation="0dp">
<View
android:id="@+id/toolbar_cutout_spacer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:theme="?actionBarStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent" />
</com.google.android.material.appbar.AppBarLayout>
</FrameLayout>

Wyświetl plik

@ -4,8 +4,8 @@
tools:viewBindingIgnore="true"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="46dp"
android:layout_height="46dp"
android:layout_width="@dimen/media_rail_item_size"
android:layout_height="@dimen/media_rail_item_size"
android:layout_margin="4dp"
android:animateLayoutChanges="true">
@ -42,14 +42,4 @@
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/rail_item_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|top"
android:layout_margin="1dp"
android:src="@drawable/ic_x_28"
android:visibility="gone"
tools:visibility="visible"/>
</FrameLayout>

Wyświetl plik

@ -243,4 +243,5 @@
<dimen name="exo_media_preview_button_height">48dp</dimen>
<dimen name="media_preview_button_horizontal_margin">8dp</dimen>
<dimen name="media_preview_lottie_button_dimen">36dp</dimen>
<dimen name="media_rail_item_size">46dp</dimen>
</resources>