Fade in media detail view.

main
Nicholas 2022-11-04 13:13:14 -04:00 zatwierdzone przez Cody Henthorne
rodzic 2856697109
commit 7ad6d95b27
12 zmienionych plików z 197 dodań i 252 usunięć

Wyświetl plik

@ -21,11 +21,14 @@ import androidx.annotation.UiThread;
import androidx.appcompat.widget.AppCompatImageView;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.TransitionOptions;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import org.signal.core.util.logging.Log;
@ -418,13 +421,21 @@ public class ThumbnailView extends FrameLayout {
}
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height) {
return setImageResource(glideRequests, uri, width, height, true, null);
}
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height, boolean animate, @Nullable RequestListener<Drawable> listener) {
SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
GlideRequest request = glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transition(withCrossFade());
GlideRequest<Drawable> request = glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.listener(listener);
if (animate) {
request = request.transition(withCrossFade());
}
if (width > 0 && height > 0) {
request = request.override(width, height);

Wyświetl plik

@ -51,11 +51,6 @@ public final class ImageMediaPreviewFragment extends MediaPreviewFragment {
@Override
public void pause() {}
@Override
public ViewGroup getBottomBarControls() {
return bottomBarControlView;
}
@Override
public void setBottomButtonControls(MediaPreviewPlayerControlView playerControlView) {
bottomBarControlView = playerControlView;

Wyświetl plik

@ -81,7 +81,6 @@ public abstract class MediaPreviewFragment extends Fragment {
public abstract void cleanUp();
public abstract void pause();
public abstract ViewGroup getBottomBarControls();
public abstract void setBottomButtonControls(MediaPreviewPlayerControlView playerControlView);
private void checkMediaStillAvailable() {

Wyświetl plik

@ -5,18 +5,24 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.GONE
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewGroup.VISIBLE
import android.view.animation.PathInterpolator
import android.widget.Toast
import androidx.core.app.ShareCompat
import androidx.core.view.ViewCompat
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.RecyclerView
import androidx.viewpager2.widget.MarginPageTransformer
import androidx.viewpager2.widget.ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT
@ -34,6 +40,7 @@ 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.mediasend.Media
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
import org.thoughtcrime.securesms.mms.GlideApp
@ -48,8 +55,8 @@ import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.SaveAttachmentTask
import org.thoughtcrime.securesms.util.StorageUtil
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.visible
import java.util.Locale
import java.util.Optional
import java.util.concurrent.TimeUnit
class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), MediaPreviewFragment.Events {
@ -60,8 +67,8 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
private val viewModel: MediaPreviewV2ViewModel by viewModels()
private val debouncer = Debouncer(2, TimeUnit.SECONDS)
private lateinit var fullscreenHelper: FullscreenHelper
private lateinit var albumRailAdapter: MediaRailAdapter
override fun onAttach(context: Context) {
super.onAttach(context)
@ -80,6 +87,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
initializeViewModel(args)
initializeToolbar(binding.toolbar)
initializeViewPager()
initializeAlbumRail()
initializeFullScreenUi()
anchorMarginsToBottomInsets(binding.mediaPreviewDetailsContainer)
lifecycleDisposable += viewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe {
@ -123,24 +131,30 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
})
}
private fun initializeAlbumRail(recyclerView: RecyclerView, albumThumbnailMedia: List<Media?>, albumPosition: Int) {
recyclerView.itemAnimator = null // Or can crash when set to INVISIBLE while animating by FullscreenHelper https://issuetracker.google.com/issues/148720682
val mediaRailAdapter = MediaRailAdapter(
GlideApp.with(this),
object : MediaRailAdapter.RailItemListener {
override fun onRailItemClicked(distanceFromActive: Int) {
binding.mediaPager.currentItem += distanceFromActive
}
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)
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.")
override fun onRailItemDeleteClicked(distanceFromActive: Int) {
throw UnsupportedOperationException("Callback unsupported.")
}
},
false,
object : ImageLoadingListener() {
override fun onAllRequestsFinished() {
crossfadeViewIn(this@apply)
}
}
},
false
)
mediaRailAdapter.setMedia(albumThumbnailMedia, albumPosition)
recyclerView.adapter = mediaRailAdapter
recyclerView.smoothScrollToPosition(albumPosition)
)
this.adapter = albumRailAdapter
}
}
private fun initializeFullScreenUi() {
@ -187,6 +201,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
onMediaNotAvailable()
return
}
val currentPosition = currentState.position
val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentPosition]
@ -197,16 +212,29 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
}
}
val mediaType: MediaPreviewPlayerControlView.MediaMode = if (currentItem.attachment?.isVideoGif == true) {
MediaPreviewPlayerControlView.MediaMode.IMAGE
} else {
MediaPreviewPlayerControlView.MediaMode.fromString(currentItem.contentType)
}
binding.mediaPreviewPlaybackControls.setMediaMode(mediaType)
bindTextViews(currentItem, currentState.showThread)
bindMenuItems(currentItem)
bindMediaPreviewPlaybackControls(currentItem, getMediaPreviewFragmentFromChildFragmentManager(currentPosition))
binding.toolbar.title = getTitleText(currentItem, currentState.showThread)
val albumThumbnailMedia: List<Media> = if (currentState.allMediaInAlbumRail) {
currentState.mediaRecords.mapNotNull { it.toMedia() }
} else {
currentState.albums[currentItem.attachment?.mmsId] ?: emptyList()
}
bindAlbumRail(albumThumbnailMedia, currentItem)
crossfadeViewIn(binding.mediaPreviewDetailsContainer)
}
private fun bindTextViews(currentItem: MediaDatabase.MediaRecord, showThread: Boolean) {
binding.toolbar.title = getTitleText(currentItem, showThread)
binding.toolbar.subtitle = getSubTitleText(currentItem)
val caption = currentItem.attachment?.caption
binding.mediaPreviewCaption.text = caption
binding.mediaPreviewCaption.visible = caption != null
}
private fun bindMenuItems(currentItem: MediaDatabase.MediaRecord) {
val menu: Menu = binding.toolbar.menu
if (currentItem.threadId == MediaIntentFactory.NOT_IN_A_THREAD.toLong()) {
menu.findItem(R.id.delete).isVisible = false
@ -222,38 +250,50 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
}
return@setOnMenuItemClickListener true
}
val albumThumbnailMedia = if (currentState.allMediaInAlbumRail) {
currentState.mediaRecords.map { it.toMedia() }
}
private fun bindMediaPreviewPlaybackControls(currentItem: MediaDatabase.MediaRecord, currentFragment: MediaPreviewFragment?) {
val mediaType: MediaPreviewPlayerControlView.MediaMode = if (currentItem.attachment?.isVideoGif == true) {
MediaPreviewPlayerControlView.MediaMode.IMAGE
} else {
currentState.mediaRecords
.filter { it.attachment != null && it.attachment!!.mmsId == currentItem.attachment?.mmsId }
.map { it.toMedia() }
MediaPreviewPlayerControlView.MediaMode.fromString(currentItem.contentType)
}
val caption = currentItem.attachment?.caption
val albumRailEnabled = albumThumbnailMedia.size > 1
if (caption != null) {
binding.mediaPreviewCaption.text = caption
binding.mediaPreviewCaption.visibility = View.VISIBLE
} else {
binding.mediaPreviewCaption.visibility = View.GONE
}
binding.mediaPreviewPlaybackControls.setMediaMode(mediaType)
binding.mediaPreviewPlaybackControls.setShareButtonListener { share(currentItem) }
binding.mediaPreviewPlaybackControls.setForwardButtonListener { forward(currentItem) }
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.findViewById(R.id.media_preview_album_rail)
if (albumRailEnabled) {
val albumPosition = albumThumbnailMedia.indexOfFirst { it?.uri == currentItem.attachment?.uri }
initializeAlbumRail(albumRail, albumThumbnailMedia, albumPosition)
}
albumRail.visibility = if (albumRailEnabled) View.VISIBLE else View.GONE
val currentFragment: MediaPreviewFragment? = getMediaPreviewFragmentFromChildFragmentManager(currentPosition)
currentFragment?.setBottomButtonControls(binding.mediaPreviewPlaybackControls)
}
private fun bindAlbumRail(albumThumbnailMedia: List<Media>, currentItem: MediaDatabase.MediaRecord) {
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
if (albumThumbnailMedia.size > 1) {
val albumPosition = albumThumbnailMedia.indexOfFirst { it.uri == currentItem.attachment?.uri }
if (albumRail.visibility == GONE) {
albumRail.visibility = View.INVISIBLE
}
albumRailAdapter.setMedia(albumThumbnailMedia, albumPosition)
albumRail.smoothScrollToPosition(albumPosition)
} else {
albumRail.visibility = View.GONE
albumRailAdapter.setMedia(emptyList())
}
}
private fun crossfadeViewIn(view: View, duration: Long = 200) {
if (!view.isVisible) {
val viewPropertyAnimator = view.animate()
.alpha(1f)
.setDuration(duration)
.withStartAction {
view.visibility = VISIBLE
}
if (Build.VERSION.SDK_INT >= 21) {
viewPropertyAnimator.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
}
viewPropertyAnimator.start()
}
}
private fun getMediaPreviewFragmentFromChildFragmentManager(currentPosition: Int) = childFragmentManager.findFragmentByTag("f$currentPosition") as? MediaPreviewFragment
private fun getTitleText(mediaRecord: MediaDatabase.MediaRecord, showThread: Boolean): String {
@ -304,29 +344,6 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
}
}
private fun MediaDatabase.MediaRecord.toMedia(): Media? {
val attachment = this.attachment
val uri = attachment?.uri
if (attachment == null || uri == null) {
return null
}
return Media(
uri,
this.contentType,
this.date,
attachment.width,
attachment.height,
attachment.size,
0,
attachment.isBorderless,
attachment.isVideoGif,
Optional.empty(),
Optional.ofNullable(attachment.caption),
Optional.empty()
)
}
override fun singleTapOnMedia(): Boolean {
fullscreenHelper.toggleUiVisibility()
return true

Wyświetl plik

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.mediapreview
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.mediasend.Media
data class MediaPreviewV2State(
val mediaRecords: List<MediaDatabase.MediaRecord> = emptyList(),
@ -9,6 +10,7 @@ data class MediaPreviewV2State(
val showThread: Boolean = false,
val allMediaInAlbumRail: Boolean = false,
val leftIsRecent: Boolean = false,
val albums: Map<Long, List<Media>> = mapOf(),
) {
enum class LoadState { INIT, DATA_LOADED, MEDIA_READY }
}

Wyświetl plik

@ -13,8 +13,10 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.util.AttachmentUtil
import org.thoughtcrime.securesms.util.rx.RxStore
import java.util.Optional
class MediaPreviewV2ViewModel : ViewModel() {
private val TAG = Log.tag(MediaPreviewV2ViewModel::class.java)
@ -27,16 +29,26 @@ class MediaPreviewV2ViewModel : ViewModel() {
fun fetchAttachments(startingAttachmentId: AttachmentId, threadId: Long, sorting: MediaDatabase.Sorting, forceRefresh: Boolean = false) {
if (store.state.loadState == MediaPreviewV2State.LoadState.INIT || forceRefresh) {
disposables += store.update(repository.getAttachments(startingAttachmentId, threadId, sorting)) { result: MediaPreviewRepository.Result, oldState: MediaPreviewV2State ->
val albums = result.records.fold(mutableMapOf()) { acc: MutableMap<Long, MutableList<Media>>, mediaRecord: MediaDatabase.MediaRecord ->
val attachment = mediaRecord.attachment
if (attachment != null) {
val convertedMedia = mediaRecord.toMedia() ?: return@fold acc
acc.getOrPut(attachment.mmsId) { mutableListOf() }.add(convertedMedia)
}
acc
}
if (oldState.leftIsRecent) {
oldState.copy(
position = result.initialPosition,
mediaRecords = result.records,
albums = albums,
loadState = MediaPreviewV2State.LoadState.DATA_LOADED,
)
} else {
oldState.copy(
position = result.records.size - result.initialPosition - 1,
mediaRecords = result.records.reversed(),
albums = albums.mapValues { it.value.reversed() },
loadState = MediaPreviewV2State.LoadState.DATA_LOADED,
)
}
@ -82,3 +94,26 @@ class MediaPreviewV2ViewModel : ViewModel() {
}
}
}
fun MediaDatabase.MediaRecord.toMedia(): Media? {
val attachment = this.attachment
val uri = attachment?.uri
if (attachment == null || uri == null) {
return null
}
return Media(
uri,
this.contentType,
this.date,
attachment.width,
attachment.height,
attachment.size,
0,
attachment.isBorderless,
attachment.isVideoGif,
Optional.empty(),
Optional.ofNullable(attachment.caption),
Optional.empty()
)
}

Wyświetl plik

@ -1,148 +0,0 @@
package org.thoughtcrime.securesms.mediapreview;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.mediasend.Media;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
public class MediaPreviewViewModel extends ViewModel {
private final MutableLiveData<PreviewData> previewData = new MutableLiveData<>();
private boolean leftIsRecent;
private @Nullable Cursor cursor;
public void setCursor(@NonNull Context context, @Nullable Cursor cursor, boolean leftIsRecent) {
boolean firstLoad = (this.cursor == null) && (cursor != null);
this.cursor = cursor;
this.leftIsRecent = leftIsRecent;
if (firstLoad) {
setActiveAlbumRailItem(context, 0);
}
}
public void setActiveAlbumRailItem(@NonNull Context context, int activePosition) {
if (cursor == null) {
previewData.postValue(new PreviewData(Collections.emptyList(), null, 0));
return;
}
activePosition = getCursorPosition(activePosition);
cursor.moveToPosition(activePosition);
MediaRecord activeRecord = MediaRecord.from(cursor);
LinkedList<Media> rail = new LinkedList<>();
Media activeMedia = toMedia(activeRecord);
if (activeMedia != null) rail.add(activeMedia);
while (cursor.moveToPrevious()) {
MediaRecord record = MediaRecord.from(cursor);
if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) {
Media media = toMedia(record);
if (media != null) rail.addFirst(media);
} else {
break;
}
}
cursor.moveToPosition(activePosition);
while (cursor.moveToNext()) {
MediaRecord record = MediaRecord.from(cursor);
if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) {
Media media = toMedia(record);
if (media != null) rail.addLast(media);
} else {
break;
}
}
if (!leftIsRecent) {
Collections.reverse(rail);
}
previewData.postValue(new PreviewData(rail.size() > 1 ? rail : Collections.emptyList(),
activeRecord.getAttachment().getCaption(),
rail.indexOf(activeMedia)));
}
public void resubmitPreviewData() {
previewData.postValue(previewData.getValue());
}
private int getCursorPosition(int position) {
if (cursor == null) {
return 0;
}
if (leftIsRecent) return position;
else return cursor.getCount() - 1 - position;
}
private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) {
Uri uri = mediaRecord.getAttachment().getUri();
if (uri == null) {
return null;
}
return new Media(uri,
mediaRecord.getContentType(),
mediaRecord.getDate(),
mediaRecord.getAttachment().getWidth(),
mediaRecord.getAttachment().getHeight(),
mediaRecord.getAttachment().getSize(),
0,
mediaRecord.getAttachment().isBorderless(),
mediaRecord.getAttachment().isVideoGif(),
Optional.empty(),
Optional.ofNullable(mediaRecord.getAttachment().getCaption()),
Optional.empty());
}
public LiveData<PreviewData> getPreviewData() {
return previewData;
}
public static class PreviewData {
private final List<Media> albumThumbnails;
private final String caption;
private final int activePosition;
public PreviewData(@NonNull List<Media> albumThumbnails, @Nullable String caption, int activePosition) {
this.albumThumbnails = albumThumbnails;
this.caption = caption;
this.activePosition = activePosition;
}
public @NonNull List<Media> getAlbumThumbnails() {
return albumThumbnails;
}
public @Nullable String getCaption() {
return caption;
}
public int getActivePosition() {
return activePosition;
}
}
}

Wyświetl plik

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.mediapreview;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -8,6 +9,11 @@ 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;
@ -16,6 +22,7 @@ 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> {
@ -26,19 +33,21 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
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;
private RailItemAddListener addListener;
private int activePosition;
private boolean editable;
private boolean interactive;
public MediaRailAdapter(@NonNull GlideRequests glideRequests, @NonNull RailItemListener listener, boolean editable) {
this.glideRequests = glideRequests;
this.media = new ArrayList<>();
this.listener = listener;
this.editable = editable;
this.stableIdGenerator = new StableIdGenerator<>();
this.interactive = true;
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);
}
@ -60,7 +69,7 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
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);
((MediaViewHolder) viewHolder).bind(media.get(i), i == activePosition, glideRequests, listener, i - activePosition, editable, interactive, imageLoadingListener);
break;
case TYPE_BUTTON:
((ButtonViewHolder) viewHolder).bind(addListener);
@ -159,9 +168,10 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
void bind(@NonNull Media media, boolean isActive, @NonNull GlideRequests glideRequests,
@NonNull RailItemListener railItemListener, int distanceFromActive, boolean editable,
boolean interactive)
boolean interactive, @NonNull ImageLoadingListener listener)
{
image.setImageResource(glideRequests, media.getUri());
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);
@ -208,4 +218,32 @@ public class MediaRailAdapter extends RecyclerView.Adapter<MediaRailAdapter.Medi
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

@ -142,11 +142,6 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
}
}
@Override
public @Nullable ViewGroup getBottomBarControls() {
return videoView.getControlView();
}
@Override
public void setBottomButtonControls(@NonNull MediaPreviewPlayerControlView playerControlView) {
videoView.setControlView(playerControlView);

Wyświetl plik

@ -6,7 +6,6 @@ import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.view.KeyEvent
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.TextView
import androidx.activity.OnBackPressedCallback

Wyświetl plik

@ -51,7 +51,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_marginTop="11dp"

Wyświetl plik

@ -18,7 +18,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:animateLayoutChanges="true"
android:visibility="invisible"
android:alpha="0"
android:animateLayoutChanges="false"
android:background="@color/signal_dark_colorSurface_87"
android:gravity="bottom"
android:orientation="vertical"