Improve storage management detail view descriptions.
This commit is contained in:
parent
544b75a2a7
commit
911ca7c29d
10 changed files with 264 additions and 93 deletions
|
@ -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>
|
|
@ -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 · %2$s</string>
|
||||||
|
<string name="MediaOverviewActivity_detail_line_3_part" translatable="false">%1$s · %2$s · %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>
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue