Improve storage management detail view descriptions.

This commit is contained in:
Alan Evans 2019-12-04 20:13:51 -05:00 committed by Greyson Parrelli
parent 544b75a2a7
commit 911ca7c29d
10 changed files with 264 additions and 93 deletions

View file

@ -13,34 +13,28 @@
<TextView <TextView
android:id="@+id/line1" android:id="@+id/line1"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="marquee"
android:gravity="center"
android:maxLines="1"
android:paddingStart="6dp" android:paddingStart="6dp"
android:paddingEnd="6dp" android:paddingEnd="6dp"
android:singleLine="true"
android:textSize="14sp" android:textSize="14sp"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@+id/image_container" app:layout_constraintStart_toEndOf="@+id/image_container"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Sent voice note, 02:35" tools:text="Sent voice note, 02:35" />
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/line2" android:id="@+id/line2"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="marquee"
android:gravity="center"
android:maxLines="1"
android:paddingStart="6dp" android:paddingStart="6dp"
android:paddingEnd="6dp" android:paddingEnd="6dp"
android:singleLine="true"
android:textSize="14sp" android:textSize="14sp"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@+id/image_container" app:layout_constraintStart_toEndOf="@+id/image_container"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="2.7 MB, 11.06.19 at 5:25 AM" tools:text="2.7 MB, 11.06.19 at 5:25 AM" />
tools:visibility="visible" />
</LinearLayout> </LinearLayout>

View file

@ -353,11 +353,7 @@
<string name="DeviceListItem_today">Today</string> <string name="DeviceListItem_today">Today</string>
<!-- DocumentView --> <!-- DocumentView -->
<string name="DocumentView_unknown_file">Unknown file</string>
<string name="DocumentView_unnamed_file">Unnamed file</string> <string name="DocumentView_unnamed_file">Unnamed file</string>
<string name="DocumentView_audio_file">Audio file</string>
<string name="DocumentView_image_file">Image file</string>
<string name="DocumentView_video_file">Video file</string>
<!-- DozeReminder --> <!-- DozeReminder -->
<string name="DozeReminder_optimize_for_missing_play_services">Optimize for missing Play Services</string> <string name="DozeReminder_optimize_for_missing_play_services">Optimize for missing Play Services</string>
@ -486,6 +482,9 @@
<!-- MediaOverviewActivity --> <!-- MediaOverviewActivity -->
<string name="MediaOverviewActivity_Media">Media</string> <string name="MediaOverviewActivity_Media">Media</string>
<string name="MediaOverviewActivity_Files">Files</string>
<string name="MediaOverviewActivity_Audio">Audio</string>
<string name="MediaOverviewActivity_All">All</string>
<plurals name="MediaOverviewActivity_Media_delete_confirm_title"> <plurals name="MediaOverviewActivity_Media_delete_confirm_title">
<item quantity="one">Delete selected item?</item> <item quantity="one">Delete selected item?</item>
<item quantity="other">Delete selected items?</item> <item quantity="other">Delete selected items?</item>
@ -496,11 +495,8 @@
</plurals> </plurals>
<string name="MediaOverviewActivity_Media_delete_progress_title">Deleting</string> <string name="MediaOverviewActivity_Media_delete_progress_title">Deleting</string>
<string name="MediaOverviewActivity_Media_delete_progress_message">Deleting messages…</string> <string name="MediaOverviewActivity_Media_delete_progress_message">Deleting messages…</string>
<string name="MediaOverviewActivity_Files">Files</string>
<string name="MediaOverviewActivity_Select_all">Select all</string> <string name="MediaOverviewActivity_Select_all">Select all</string>
<string name="MediaOverviewActivity_collecting_attachments">Collecting attachments…</string> <string name="MediaOverviewActivity_collecting_attachments">Collecting attachments…</string>
<string name="MediaOverviewActivity_Audio">Audio</string>
<string name="MediaOverviewActivity_All">All</string>
<string name="MediaOverviewActivity_Sort_by">Sort by</string> <string name="MediaOverviewActivity_Sort_by">Sort by</string>
<string name="MediaOverviewActivity_Newest">Newest</string> <string name="MediaOverviewActivity_Newest">Newest</string>
<string name="MediaOverviewActivity_Oldest">Oldest</string> <string name="MediaOverviewActivity_Oldest">Oldest</string>
@ -510,6 +506,18 @@
<string name="MediaOverviewActivity_List_view_description">List view</string> <string name="MediaOverviewActivity_List_view_description">List view</string>
<string name="MediaOverviewActivity_Selected_description">Selected</string> <string name="MediaOverviewActivity_Selected_description">Selected</string>
<string name="MediaOverviewActivity_file">File</string>
<string name="MediaOverviewActivity_audio">Audio</string>
<string name="MediaOverviewActivity_video">Video</string>
<string name="MediaOverviewActivity_image">Image</string>
<string name="MediaOverviewActivity_detail_line_2_part" translatable="false">%1$s &#183; %2$s</string>
<string name="MediaOverviewActivity_detail_line_3_part" translatable="false">%1$s &#183; %2$s &#183; %3$s</string>
<string name="MediaOverviewActivity_sent_by_s">Sent by %1$s</string>
<string name="MediaOverviewActivity_sent_by_you">Sent by you</string>
<string name="MediaOverviewActivity_sent_by_s_to_s">Sent by %1$s to %2$s</string>
<string name="MediaOverviewActivity_sent_by_you_to_s">Sent by you to %1$s</string>
<!--- NotificationBarManager --> <!--- NotificationBarManager -->
<string name="NotificationBarManager_signal_call_in_progress">Signal call in progress</string> <string name="NotificationBarManager_signal_call_in_progress">Signal call in progress</string>
<string name="NotificationBarManager__establishing_signal_call">Establishing Signal call</string> <string name="NotificationBarManager__establishing_signal_call">Establishing Signal call</string>

View file

@ -52,7 +52,6 @@ import androidx.viewpager.widget.ViewPager;
import org.thoughtcrime.securesms.animation.DepthPageTransformer; import org.thoughtcrime.securesms.animation.DepthPageTransformer;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener; import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader; import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
@ -722,13 +721,12 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
MediaRecord mediaRecord = MediaRecord.from(context, cursor); MediaRecord mediaRecord = MediaRecord.from(context, cursor);
RecipientId recipientId = mediaRecord.getRecipientId(); RecipientId recipientId = mediaRecord.getRecipientId();
RecipientId threadRecipientId = DatabaseFactory.getThreadDatabase(context) RecipientId threadRecipientId = mediaRecord.getThreadRecipientId();
.getRecipientIdForThreadId(mediaRecord.getThreadId());
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError(); if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
return new MediaItem(Recipient.live(recipientId).get(), return new MediaItem(Recipient.live(recipientId).get(),
threadRecipientId != null ? Recipient.live(threadRecipientId).get() : null, Recipient.live(threadRecipientId).get(),
mediaRecord.getAttachment(), mediaRecord.getAttachment(),
mediaRecord.getAttachment().getDataUri(), mediaRecord.getAttachment().getDataUri(),
mediaRecord.getContentType(), mediaRecord.getContentType(),

View file

@ -5,6 +5,7 @@ import android.database.ContentObserver;
import android.database.Cursor; import android.database.Cursor;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase;
@ -17,7 +18,8 @@ import java.util.List;
public class MediaDatabase extends Database { public class MediaDatabase extends Database {
public static final int ALL_THREADS = -1; public static final int ALL_THREADS = -1;
private static final String THREAD_RECIPIENT_ID = "THREAD_RECIPIENT_ID";
private static final String BASE_MEDIA_QUERY = "SELECT " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ROW_ID + ", " private static final String BASE_MEDIA_QUERY = "SELECT " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ROW_ID + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", "
@ -48,9 +50,12 @@ public class MediaDatabase extends Database {
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.THREAD_ID + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.THREAD_ID + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.RECIPIENT_ID + " " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.RECIPIENT_ID + ", "
+ ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " as " + THREAD_RECIPIENT_ID + " "
+ "FROM " + AttachmentDatabase.TABLE_NAME + " LEFT JOIN " + MmsDatabase.TABLE_NAME + "FROM " + AttachmentDatabase.TABLE_NAME + " LEFT JOIN " + MmsDatabase.TABLE_NAME
+ " ON " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " " + " ON " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " "
+ "LEFT JOIN " + ThreadDatabase.TABLE_NAME
+ " ON " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.THREAD_ID + " "
+ "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID + "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID
+ " FROM " + MmsDatabase.TABLE_NAME + " FROM " + MmsDatabase.TABLE_NAME
+ " WHERE " + MmsDatabase.THREAD_ID + " __EQUALITY__ ?) AND (%s) AND " + " WHERE " + MmsDatabase.THREAD_ID + " __EQUALITY__ ?) AND (%s) AND "
@ -167,16 +172,24 @@ public class MediaDatabase extends Database {
private final DatabaseAttachment attachment; private final DatabaseAttachment attachment;
private final RecipientId recipientId; private final RecipientId recipientId;
private final RecipientId threadRecipientId;
private final long threadId; private final long threadId;
private final long date; private final long date;
private final boolean outgoing; private final boolean outgoing;
private MediaRecord(DatabaseAttachment attachment, @NonNull RecipientId recipientId, long threadId, long date, boolean outgoing) { private MediaRecord(@Nullable DatabaseAttachment attachment,
this.attachment = attachment; @NonNull RecipientId recipientId,
this.recipientId = recipientId; @NonNull RecipientId threadRecipientId,
this.threadId = threadId; long threadId,
this.date = date; long date,
this.outgoing = outgoing; boolean outgoing)
{
this.attachment = attachment;
this.recipientId = recipientId;
this.threadRecipientId = threadRecipientId;
this.threadId = threadId;
this.date = date;
this.outgoing = outgoing;
} }
public static MediaRecord from(@NonNull Context context, @NonNull Cursor cursor) { public static MediaRecord from(@NonNull Context context, @NonNull Cursor cursor) {
@ -194,10 +207,17 @@ public class MediaDatabase extends Database {
date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE_RECEIVED)); date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE_RECEIVED));
} }
return new MediaRecord(attachments != null && attachments.size() > 0 ? attachments.get(0) : null, recipientId, threadId, date, outgoing); RecipientId threadRecipient = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_RECIPIENT_ID)));
return new MediaRecord(attachments != null && attachments.size() > 0 ? attachments.get(0) : null,
recipientId,
threadRecipient,
threadId,
date,
outgoing);
} }
public DatabaseAttachment getAttachment() { public @Nullable DatabaseAttachment getAttachment() {
return attachment; return attachment;
} }
@ -209,6 +229,10 @@ public class MediaDatabase extends Database {
return recipientId; return recipientId;
} }
public @NonNull RecipientId getThreadRecipientId() {
return threadRecipientId;
}
public long getThreadId() { public long getThreadId() {
return threadId; return threadId;
} }

View file

@ -17,12 +17,14 @@
package org.thoughtcrime.securesms.mediaoverview; package org.thoughtcrime.securesms.mediaoverview;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter; import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
@ -37,9 +39,14 @@ import org.thoughtcrime.securesms.database.loaders.GroupedThreadMediaLoader.Grou
import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.livedata.LiveDataPair;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -50,6 +57,7 @@ import java.util.Map;
final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private final Context context; private final Context context;
private final boolean showThread;
private final GlideRequests glideRequests; private final GlideRequests glideRequests;
private final ItemClickListener itemClickListener; private final ItemClickListener itemClickListener;
private final Map<AttachmentId, MediaRecord> selected = new HashMap<>(); private final Map<AttachmentId, MediaRecord> selected = new HashMap<>();
@ -63,12 +71,18 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private static final int GALLERY_DETAIL = 3; private static final int GALLERY_DETAIL = 3;
private static final int DOCUMENT_DETAIL = 4; private static final int DOCUMENT_DETAIL = 4;
public void pause(RecyclerView.ViewHolder holder) { void pause(RecyclerView.ViewHolder holder) {
if (holder instanceof AudioDetailViewHolder) { if (holder instanceof AudioDetailViewHolder) {
((AudioDetailViewHolder) holder).pause(); ((AudioDetailViewHolder) holder).pause();
} }
} }
void detach(RecyclerView.ViewHolder holder) {
if (holder instanceof SelectableViewHolder) {
((SelectableViewHolder) holder).onDetached();
}
}
private static class HeaderHolder extends HeaderViewHolder { private static class HeaderHolder extends HeaderViewHolder {
TextView textView; TextView textView;
@ -82,13 +96,15 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
GroupedThreadMedia media, GroupedThreadMedia media,
ItemClickListener clickListener, ItemClickListener clickListener,
boolean showFileSizes) boolean showFileSizes,
boolean showThread)
{ {
this.context = context; this.context = context;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.media = media; this.media = media;
this.itemClickListener = clickListener; this.itemClickListener = clickListener;
this.showFileSizes = showFileSizes; this.showFileSizes = showFileSizes;
this.showThread = showThread;
} }
public void setMedia(GroupedThreadMedia media) { public void setMedia(GroupedThreadMedia media) {
@ -143,7 +159,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) { public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
super.onViewDetachedFromWindow(holder); super.onViewDetachedFromWindow(holder);
if (holder instanceof SelectableViewHolder) { if (holder instanceof SelectableViewHolder) {
((SelectableViewHolder) holder).detached(); ((SelectableViewHolder) holder).onDetached();
} }
} }
@ -182,8 +198,10 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
void selectAllMedia() { void selectAllMedia() {
for (int section = 0; section < media.getSectionCount(); section++) { int sectionCount = media.getSectionCount();
for (int item = 0; item < media.getSectionItemCount(section); item++) { for (int section = 0; section < sectionCount; section++) {
int sectionItemCount = media.getSectionItemCount(section);
for (int item = 0; item < sectionItemCount; item++) {
MediaRecord mediaRecord = media.get(section, item); MediaRecord mediaRecord = media.get(section, item);
selected.put(mediaRecord.getAttachment().getAttachmentId(), mediaRecord); selected.put(mediaRecord.getAttachment().getAttachmentId(), mediaRecord);
} }
@ -203,6 +221,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
private final View selectedIndicator; private final View selectedIndicator;
private MediaDatabase.MediaRecord mediaRecord; private MediaDatabase.MediaRecord mediaRecord;
private boolean bound;
SelectableViewHolder(@NonNull View itemView) { SelectableViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
@ -210,8 +229,16 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
public void bind(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) { public void bind(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) {
if (bound) {
unbind();
}
this.mediaRecord = mediaRecord; this.mediaRecord = mediaRecord;
updateSelectedView(); updateSelectedView();
bound = true;
}
void unbind() {
bound = false;
} }
private void updateSelectedView() { private void updateSelectedView() {
@ -226,7 +253,10 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
return true; return true;
} }
void detached() { void onDetached() {
if (bound) {
unbind();
}
} }
} }
@ -259,16 +289,22 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
@Override @Override
void detached() { void unbind() {
thumbnailView.clear(glideRequests); thumbnailView.clear(glideRequests);
super.unbind();
} }
} }
private class DetailViewHolder extends SelectableViewHolder { private abstract class DetailViewHolder extends SelectableViewHolder implements Observer<Pair<Recipient, Recipient>> {
protected final View itemView; protected final View itemView;
private final TextView line1; private final TextView line1;
private final TextView line2; private final TextView line2;
private LiveDataPair<Recipient, Recipient> liveDataPair;
private Optional<String> fileName;
private String fileTypeDescription;
private Handler handler;
private Runnable selectForMarque;
DetailViewHolder(@NonNull View itemView) { DetailViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
@ -281,27 +317,82 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
public void bind(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) { public void bind(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) {
super.bind(context, mediaRecord, slide); super.bind(context, mediaRecord, slide);
line1.setText(getLine1(context, slide)); fileName = slide.getFileName();
line2.setText(getLine2(mediaRecord, slide)); fileTypeDescription = getFileTypeDescription(context, slide);
line1.setVisibility(View.VISIBLE);
line2.setVisibility(View.VISIBLE); line1.setText(fileName.or(fileTypeDescription));
line2.setText(getLine2(context, mediaRecord, slide));
itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord));
itemView.setOnLongClickListener(view -> onLongClick()); itemView.setOnLongClickListener(view -> onLongClick());
selectForMarque = () -> line1.setSelected(true);
handler = new Handler();
handler.postDelayed(selectForMarque, 2500);
LiveRecipient from = mediaRecord.isOutgoing() ? Recipient.self().live() : Recipient.live(mediaRecord.getRecipientId());
LiveRecipient to = Recipient.live(mediaRecord.getThreadRecipientId());
liveDataPair = new LiveDataPair<>(from.getLiveData(), to.getLiveData(), Recipient.UNKNOWN, Recipient.UNKNOWN);
liveDataPair.observeForever(this);
} }
private String getLine1(@NonNull Context context, @NonNull Slide slide) { @Override
return slide.getFileName() void unbind() {
.or(slide.getCaption()) liveDataPair.removeObserver(this);
.or(() -> describeUnnamedFile(context, slide)); handler.removeCallbacks(selectForMarque);
line1.setSelected(false);
super.unbind();
} }
private String getLine2(@NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) { private String getLine2(@NonNull Context context, @NonNull MediaDatabase.MediaRecord mediaRecord, @NonNull Slide slide) {
String date = DateUtils.formatDate(Locale.getDefault(), mediaRecord.getDate()); return context.getString(R.string.MediaOverviewActivity_detail_line_3_part,
return Util.getPrettyFileSize(slide.getFileSize()) + " " + date; Util.getPrettyFileSize(slide.getFileSize()),
getFileTypeDescription(context, slide),
DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), mediaRecord.getDate()));
} }
protected String describeUnnamedFile(@NonNull Context context, @NonNull Slide slide) { protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide){
return context.getString(R.string.DocumentView_unnamed_file); return context.getString(R.string.MediaOverviewActivity_file);
}
@Override
public void onChanged(Pair<Recipient, Recipient> fromToPair) {
line1.setText(describe(fromToPair.first(), fromToPair.second()));
}
private String describe(@NonNull Recipient from, @NonNull Recipient thread) {
if (from == Recipient.UNKNOWN && thread == Recipient.UNKNOWN) {
return fileName.or(fileTypeDescription);
}
String sentFromToString = getSentFromToString(from, thread);
if (fileName.isPresent()) {
return context.getString(R.string.MediaOverviewActivity_detail_line_2_part,
fileName.get(),
sentFromToString);
} else {
return sentFromToString;
}
}
private String getSentFromToString(@NonNull Recipient from, @NonNull Recipient thread) {
if (from.isLocalNumber() && from == thread) {
return context.getString(R.string.note_to_self);
}
if (showThread && (from.isLocalNumber() || thread.isGroup())) {
if (from.isLocalNumber()) {
return context.getString(R.string.MediaOverviewActivity_sent_by_you_to_s, thread.toShortString(context));
} else {
return context.getString(R.string.MediaOverviewActivity_sent_by_s_to_s, from.toShortString(context), thread.toShortString(context));
}
} else {
if (from.isLocalNumber()) {
return context.getString(R.string.MediaOverviewActivity_sent_by_you);
} else {
return context.getString(R.string.MediaOverviewActivity_sent_by_s, from.toShortString(context));
}
}
} }
} }
@ -345,13 +436,14 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
@Override @Override
void detached() { void unbind() {
audioView.stopPlaybackAndReset(); audioView.stopPlaybackAndReset();
super.unbind();
} }
@Override @Override
protected String describeUnnamedFile(@NonNull Context context, @NonNull Slide slide) { protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide) {
return context.getString(R.string.DocumentView_audio_file); return context.getString(R.string.MediaOverviewActivity_audio);
} }
public void pause() { public void pause() {
@ -377,15 +469,16 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
@Override @Override
protected String describeUnnamedFile(@NonNull Context context, @NonNull Slide slide) { protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide) {
if (slide.hasVideo()) return context.getString(R.string.DocumentView_video_file); if (slide.hasVideo()) return context.getString(R.string.MediaOverviewActivity_video);
if (slide.hasImage()) return context.getString(R.string.DocumentView_image_file); if (slide.hasImage()) return context.getString(R.string.MediaOverviewActivity_image);
return super.describeUnnamedFile(context, slide); return super.getFileTypeDescription(context, slide);
} }
@Override @Override
void detached() { void unbind() {
thumbnailView.clear(glideRequests); thumbnailView.clear(glideRequests);
super.unbind();
} }
} }

View file

@ -103,7 +103,8 @@ public final class MediaOverviewPageFragment extends Fragment
GlideApp.with(this), GlideApp.with(this),
new GroupedThreadMediaLoader.EmptyGroupedThreadMedia(), new GroupedThreadMediaLoader.EmptyGroupedThreadMedia(),
this, this,
sorting.isRelatedToFileSize()); sorting.isRelatedToFileSize(),
threadId == MediaDatabase.ALL_THREADS);
this.recyclerView.setAdapter(adapter); this.recyclerView.setAdapter(adapter);
this.recyclerView.setLayoutManager(gridManager); this.recyclerView.setLayoutManager(gridManager);
this.recyclerView.setHasFixedSize(true); this.recyclerView.setHasFixedSize(true);
@ -182,7 +183,16 @@ public final class MediaOverviewPageFragment extends Fragment
int childCount = recyclerView.getChildCount(); int childCount = recyclerView.getChildCount();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
adapter.pause(recyclerView.getChildViewHolder(recyclerView.getChildAt(i))); adapter.pause(recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
} }
}
@Override
public void onDestroy() {
super.onDestroy();
int childCount = recyclerView.getChildCount();
for (int i = 0; i < childCount; i++) {
adapter.detach(recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
}
} }
private void handleMediaMultiSelectClick(@NonNull MediaDatabase.MediaRecord mediaRecord) { private void handleMediaMultiSelectClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {

View file

@ -6,6 +6,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
@ -168,6 +169,10 @@ public final class LiveRecipient {
set(recipient); set(recipient);
} }
public @NonNull LiveData<Recipient> getLiveData() {
return liveData;
}
private @NonNull Recipient fetchRecipientFromDisk(RecipientId id) { private @NonNull Recipient fetchRecipientFromDisk(RecipientId id) {
RecipientSettings settings = recipientDatabase.getRecipientSettings(id); RecipientSettings settings = recipientDatabase.getRecipientSettings(id);
RecipientDetails details = settings.getGroupId() != null ? getGroupRecipientDetails(settings) RecipientDetails details = settings.getGroupId() != null ? getGroupRecipientDetails(settings)

View file

@ -4,7 +4,6 @@ import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.Transformations; import androidx.lifecycle.Transformations;
@ -14,6 +13,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.registration.service.RegistrationService; import org.thoughtcrime.securesms.registration.service.RegistrationService;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.livedata.LiveDataPair;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
import org.whispersystems.signalservice.internal.util.JsonUtil; import org.whispersystems.signalservice.internal.util.JsonUtil;
@ -201,27 +201,4 @@ public final class RegistrationViewModel extends ViewModel {
} }
}, this::setKeyBackupCurrentToken); }, this::setKeyBackupCurrentToken);
} }
public static class LiveDataPair<A, B> extends MediatorLiveData<Pair<A, B>> {
private A a;
private B b;
public LiveDataPair(LiveData<A> ld1, LiveData<B> ld2) {
setValue(new Pair<>(a, b));
addSource(ld1, (a) -> {
if(a != null) {
this.a = a;
}
setValue(new Pair<>(a, b));
});
addSource(ld2, (b) -> {
if(b != null) {
this.b = b;
}
setValue(new Pair<>(a, b));
});
}
}
} }

View file

@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -136,6 +137,10 @@ public class DateUtils extends android.text.format.DateUtils {
return getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale); return getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale);
} }
public static String formatDateWithoutDayOfWeek(@NonNull Locale locale, long timestamp) {
return getFormattedDateTime(timestamp, "MMM d yyyy", locale);
}
public static boolean isSameDay(long t1, long t2) { public static boolean isSameDay(long t1, long t2) {
return DATE_FORMAT.format(new Date(t1)).equals(DATE_FORMAT.format(new Date(t2))); return DATE_FORMAT.format(new Date(t1)).equals(DATE_FORMAT.format(new Date(t2)));
} }

View file

@ -0,0 +1,57 @@
package org.thoughtcrime.securesms.util.livedata;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import org.whispersystems.libsignal.util.Pair;
public final class LiveDataPair<A, B> extends MediatorLiveData<Pair<A, B>> {
private A a;
private B b;
public LiveDataPair(@NonNull LiveData<A> liveDataA,
@NonNull LiveData<B> liveDataB)
{
this(liveDataA, liveDataB, null, null);
}
public LiveDataPair(@NonNull LiveData<A> liveDataA,
@NonNull LiveData<B> liveDataB,
@Nullable A initialA,
@Nullable B initialB)
{
a = initialA;
b = initialB;
setValue(new Pair<>(a, b));
if (liveDataA == liveDataB) {
addSource(liveDataA, (a) -> {
if (a != null) {
this.a = a;
//noinspection unchecked: A is B if live datas are same instance
this.b = (B) a;
}
setValue(new Pair<>(a, b));
});
} else {
addSource(liveDataA, (a) -> {
if (a != null) {
this.a = a;
}
setValue(new Pair<>(a, b));
});
addSource(liveDataB, (b) -> {
if (b != null) {
this.b = b;
}
setValue(new Pair<>(a, b));
});
}
}
}