Apply style changes to shared media, color icon, and wallpaper previews.

This commit is contained in:
Alex Hart
2021-06-25 14:27:51 -03:00
committed by GitHub
parent 9dac5691f0
commit 01047f0546
31 changed files with 529 additions and 313 deletions

View File

@@ -56,17 +56,21 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private static final long SELECTION_ANIMATION_DURATION = TimeUnit.MILLISECONDS.toMillis(150);
private final Context context;
private final boolean showThread;
private final GlideRequests glideRequests;
private final ItemClickListener itemClickListener;
private final Map<AttachmentId, MediaRecord> selected = new HashMap<>();
private final Map<AttachmentId, MediaRecord> selected = new HashMap<>();
private final AudioItemListener audioItemListener;
private GroupedThreadMedia media;
@@ -74,10 +78,12 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private boolean detailView;
private static final int AUDIO_DETAIL = 1;
private static final int GALLERY = 2;
public static final int GALLERY = 2;
private static final int GALLERY_DETAIL = 3;
private static final int DOCUMENT_DETAIL = 4;
private static final int PAYLOAD_SELECTED = 1;
void detach(RecyclerView.ViewHolder holder) {
if (holder instanceof SelectableViewHolder) {
((SelectableViewHolder) holder).onDetached();
@@ -101,13 +107,13 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
boolean showFileSizes,
boolean showThread)
{
this.context = context;
this.glideRequests = glideRequests;
this.media = media;
this.itemClickListener = clickListener;
this.audioItemListener = audioItemListener;
this.showFileSizes = showFileSizes;
this.showThread = showThread;
this.context = context;
this.glideRequests = glideRequests;
this.media = media;
this.itemClickListener = clickListener;
this.audioItemListener = audioItemListener;
this.showFileSizes = showFileSizes;
this.showThread = showThread;
}
public void setMedia(GroupedThreadMedia media) {
@@ -138,16 +144,26 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
MediaDatabase.MediaRecord mediaRecord = media.get(section, offset);
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
if (slide.hasAudio()) return AUDIO_DETAIL;
if (slide.hasAudio()) return AUDIO_DETAIL;
if (slide.hasImage() || slide.hasVideo()) return detailView ? GALLERY_DETAIL : GALLERY;
if (slide.hasDocument()) return DOCUMENT_DETAIL;
if (slide.hasDocument()) return DOCUMENT_DETAIL;
return 0;
}
@Override
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int section) {
((HeaderHolder)viewHolder).textView.setText(media.getName(section));
((HeaderHolder) viewHolder).textView.setText(media.getName(section));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (holder instanceof SelectableViewHolder && payloads.contains(PAYLOAD_SELECTED)) {
SelectableViewHolder selectableViewHolder = (SelectableViewHolder) holder;
selectableViewHolder.animateSelectedView();
} else {
super.onBindViewHolder(holder, position, payloads);
}
}
@Override
@@ -155,7 +171,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
MediaDatabase.MediaRecord mediaRecord = media.get(section, offset);
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
((SelectableViewHolder)viewHolder).bind(context, mediaRecord, slide);
((SelectableViewHolder) viewHolder).bind(context, mediaRecord, slide);
}
@Override
@@ -183,7 +199,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
selected.put(attachmentId, mediaRecord);
}
notifyDataSetChanged();
notifyItemRangeChanged(0, getItemCount(), PAYLOAD_SELECTED);
}
public int getSelectedMediaCount() {
@@ -203,7 +219,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
public void clearSelection() {
selected.clear();
notifyDataSetChanged();
notifyItemRangeChanged(0, getItemCount(), PAYLOAD_SELECTED);
}
void selectAllMedia() {
@@ -215,7 +231,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
selected.put(mediaRecord.getAttachment().getAttachmentId(), mediaRecord);
}
}
this.notifyDataSetChanged();
this.notifyItemRangeChanged(0, getItemCount(), PAYLOAD_SELECTED);
}
void setShowFileSizes(boolean showFileSizes) {
@@ -228,9 +244,10 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
class SelectableViewHolder extends ItemViewHolder {
private final View selectedIndicator;
private MediaDatabase.MediaRecord mediaRecord;
private boolean bound;
protected final View selectedIndicator;
private MediaDatabase.MediaRecord mediaRecord;
private boolean bound;
SelectableViewHolder(@NonNull View itemView) {
super(itemView);
@@ -250,15 +267,27 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
bound = false;
}
private void updateSelectedView() {
protected boolean isSelected() {
return selected.containsKey(mediaRecord.getAttachment().getAttachmentId());
}
protected void updateSelectedView() {
if (selectedIndicator != null) {
selectedIndicator.setVisibility(selected.containsKey(mediaRecord.getAttachment().getAttachmentId()) ? View.VISIBLE : View.GONE);
selectedIndicator.animate().cancel();
selectedIndicator.setAlpha(isSelected() ? 1f : 0f);
}
}
protected void animateSelectedView() {
if (selectedIndicator != null) {
selectedIndicator.animate()
.alpha(isSelected() ? 1f : 0f)
.setDuration(SELECTION_ANIMATION_DURATION);
}
}
boolean onLongClick() {
itemClickListener.onMediaLongClicked(mediaRecord);
updateSelectedView();
return true;
}
@@ -271,6 +300,9 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private class GalleryViewHolder extends SelectableViewHolder {
private static final float SCALE_SELECTED = 0.83f;
private static final float SCALE_NORMAL = 1f;
private final ThumbnailView thumbnailView;
private final TextView imageFileSize;
@@ -296,18 +328,40 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
thumbnailView.setOnLongClickListener(view -> onLongClick());
}
@Override
protected void updateSelectedView() {
super.updateSelectedView();
thumbnailView.animate().cancel();
float scale = isSelected() ? SCALE_SELECTED : SCALE_NORMAL;
thumbnailView.setScaleX(scale);
thumbnailView.setScaleY(scale);
}
@Override
void unbind() {
thumbnailView.clear(glideRequests);
super.unbind();
}
@Override
public void animateSelectedView() {
super.animateSelectedView();
float scale = isSelected() ? SCALE_SELECTED : SCALE_NORMAL;
thumbnailView.animate()
.scaleX(scale)
.scaleY(scale)
.setDuration(SELECTION_ANIMATION_DURATION);
}
}
private abstract class DetailViewHolder extends SelectableViewHolder implements Observer<Pair<Recipient, Recipient>> {
protected final View itemView;
private final TextView line1;
private final TextView line2;
private final TextView line1;
private final TextView line2;
private LiveDataPair<Recipient, Recipient> liveDataPair;
private Optional<String> fileName;
private String fileTypeDescription;
@@ -333,7 +387,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord));
itemView.setOnLongClickListener(view -> onLongClick());
selectForMarque = () -> line1.setSelected(true);
handler = new Handler(Looper.getMainLooper());
handler = new Handler(Looper.getMainLooper());
handler.postDelayed(selectForMarque, 2500);
LiveRecipient from = mediaRecord.isOutgoing() ? Recipient.self().live() : Recipient.live(mediaRecord.getRecipientId());
@@ -358,7 +412,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), mediaRecord.getDate()));
}
protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide){
protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide) {
return context.getString(R.string.MediaOverviewActivity_file);
}
@@ -529,15 +583,21 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
interface ItemClickListener {
void onMediaClicked(@NonNull MediaDatabase.MediaRecord mediaRecord);
void onMediaLongClicked(MediaDatabase.MediaRecord mediaRecord);
}
interface AudioItemListener {
void onPlay(@NonNull Uri audioUri, double progress, long messageId);
void onPause(@NonNull Uri audioUri);
void onSeekTo(@NonNull Uri audioUri, double progress);
void onStopAndReset(@NonNull Uri audioUri);
void registerPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer);
void unregisterPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer);
}
}

View File

@@ -0,0 +1,59 @@
package org.thoughtcrime.securesms.mediaoverview
import android.graphics.Rect
import android.view.View
import androidx.annotation.Px
import androidx.recyclerview.widget.RecyclerView
import org.thoughtcrime.securesms.util.ViewUtil
internal class MediaGridDividerDecoration(
private val spanCount: Int,
@Px private val space: Int,
private val adapter: MediaGalleryAllAdapter
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val holder = parent.getChildViewHolder(view)
val adapterPosition = holder.adapterPosition
val section = adapter.getAdapterPositionSection(adapterPosition)
val itemSectionOffset = adapter.getItemSectionOffset(section, adapterPosition)
if (itemSectionOffset == -1) {
return
}
val sectionItemViewType = adapter.getSectionItemViewType(section, itemSectionOffset)
if (sectionItemViewType != MediaGalleryAllAdapter.GALLERY) {
return
}
val column = itemSectionOffset % spanCount
val isRtl = ViewUtil.isRtl(view)
val distanceFromEnd = spanCount - 1 - column
val spaceStart = (column / spanCount.toFloat()) * space
val spaceEnd = (distanceFromEnd / spanCount.toFloat()) * space
outRect.setStart(spaceStart.toInt(), isRtl)
outRect.setEnd(spaceEnd.toInt(), isRtl)
outRect.bottom = space
}
private fun Rect.setEnd(end: Int, isRtl: Boolean) {
if (isRtl) {
left = end
} else {
right = end
}
}
private fun Rect.setStart(start: Int, isRtl: Boolean) {
if (isRtl) {
right = start
} else {
left = start
}
}
}

View File

@@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.mediaoverview;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
@@ -28,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
@@ -96,8 +98,8 @@ public final class MediaOverviewActivity extends PassphraseRequiredActivity {
boolean allThreads = threadId == MediaDatabase.ALL_THREADS;
tabLayout.addOnTabSelectedListener(new OnTabSelectedListener());
fillTabLayoutIfFits(tabLayout);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager()));
@@ -283,4 +285,40 @@ public final class MediaOverviewActivity extends PassphraseRequiredActivity {
return pages.get(position).second();
}
}
private static final class OnTabSelectedListener implements TabLayout.OnTabSelectedListener {
private final Typeface tabUnselected = Typeface.create("sans-serif", Typeface.NORMAL);
private final Typeface tabSelected = Typeface.create("sans-serif-medium", Typeface.NORMAL);
@Override
public void onTabSelected(@NonNull TabLayout.Tab tab) {
View view = getCustomView(tab);
TextView title = view.findViewById(android.R.id.text1);
title.setTypeface(tabSelected);
title.setTextColor(ContextCompat.getColor(view.getContext(), R.color.signal_inverse_primary));
}
@Override
public void onTabUnselected(@NonNull TabLayout.Tab tab) {
View view = getCustomView(tab);
TextView title = view.findViewById(android.R.id.text1);
title.setTypeface(tabUnselected);
title.setTextColor(ContextCompat.getColor(view.getContext(), R.color.signal_text_secondary));
}
@Override
public void onTabReselected(@NonNull TabLayout.Tab tab) {
// Intentionally Blank.
}
private @NonNull View getCustomView(@NonNull TabLayout.Tab tab) {
View customView = tab.getCustomView();
if (customView == null) {
tab.setCustomView(R.layout.custom_tab_layout_text);
}
return tab.getCustomView();
}
}
}

View File

@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
public final class MediaOverviewPageFragment extends Fragment
@@ -109,10 +110,11 @@ public final class MediaOverviewPageFragment extends Fragment
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context context = requireContext();
View view = inflater.inflate(R.layout.media_overview_page_fragment, container, false);
int spans = getResources().getInteger(R.integer.media_overview_cols);
this.recyclerView = view.findViewById(R.id.media_grid);
this.noMedia = view.findViewById(R.id.no_images);
this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols));
this.gridManager = new StickyHeaderGridLayoutManager(spans);
this.adapter = new MediaGalleryAllAdapter(context,
GlideApp.with(this),
@@ -124,6 +126,7 @@ public final class MediaOverviewPageFragment extends Fragment
this.recyclerView.setAdapter(adapter);
this.recyclerView.setLayoutManager(gridManager);
this.recyclerView.setHasFixedSize(true);
this.recyclerView.addItemDecoration(new MediaGridDividerDecoration(spans, ViewUtil.dpToPx(4), adapter));
MediaOverviewViewModel viewModel = MediaOverviewViewModel.getMediaOverviewViewModel(requireActivity());
@@ -264,7 +267,6 @@ public final class MediaOverviewPageFragment extends Fragment
@Override
public void onMediaLongClicked(MediaDatabase.MediaRecord mediaRecord) {
((MediaGalleryAllAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
recyclerView.getAdapter().notifyDataSetChanged();
if (actionMode == null) {
enterMultiSelect();