diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java index 229528f98b..94e7528eff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.components; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PorterDuff; @@ -33,10 +34,12 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.audio.AudioWaveForms; import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.SlideClickListener; @@ -246,6 +249,22 @@ public final class AudioView extends FrameLayout { } this.audioSlide = audio; + + if (SignalStore.internal().getShowArchiveStateHint() && audioSlide.asAttachment() instanceof DatabaseAttachment) { + DatabaseAttachment dbAttachment = (DatabaseAttachment) audioSlide.asAttachment(); + View mediaArchive = findViewById(R.id.thumbnail_media_archive); + if (mediaArchive != null) { + mediaArchive.setVisibility(View.VISIBLE); + switch (dbAttachment.archiveTransferState) { + case NONE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLACK)); + case COPY_PENDING -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE)); + case UPLOAD_IN_PROGRESS -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.CYAN)); + case FINISHED -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.GREEN)); + case TEMPORARY_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.YELLOW)); + case PERMANENT_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); + } + } + } } public void setDownloadClickListener(@Nullable SlideClickListener listener) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java b/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java index 7b7b6ba3ba..c0479baf8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PorterDuff; @@ -23,8 +24,10 @@ import org.greenrobot.eventbus.ThreadMode; import org.signal.core.util.ByteSize; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.mms.SlidesClickedListener; @@ -138,6 +141,20 @@ public class DocumentView extends FrameLayout { this.fileSize.setText(new ByteSize(documentSlide.getFileSize()).toUnitString(2)); this.document.setText(documentSlide.getFileType(getContext()).orElse("").toLowerCase()); this.setOnClickListener(new OpenClickedListener(documentSlide)); + + if (SignalStore.internal().getShowArchiveStateHint() && documentSlide.asAttachment() instanceof DatabaseAttachment) { + DatabaseAttachment dbAttachment = (DatabaseAttachment) documentSlide.asAttachment(); + View mediaArchive = findViewById(R.id.thumbnail_media_archive); + mediaArchive.setVisibility(View.VISIBLE); + switch (dbAttachment.archiveTransferState) { + case NONE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLACK)); + case COPY_PENDING -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE)); + case UPLOAD_IN_PROGRESS -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.CYAN)); + case FINISHED -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.GREEN)); + case TEMPORARY_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.YELLOW)); + case PERMANENT_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); + } + } } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index 9e7f9fc6f7..01c1327c19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -6,6 +6,7 @@ package org.thoughtcrime.securesms.components; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -45,6 +46,8 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.components.transfercontrols.TransferControlView; import org.thoughtcrime.securesms.database.AttachmentTable; +import org.thoughtcrime.securesms.keyvalue.InternalValues; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -55,6 +58,7 @@ import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.stories.StoryTextPostModel; import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.views.Stub; @@ -415,7 +419,21 @@ public class ThumbnailView extends FrameLayout { Attachment slideAttachment = slide.asAttachment(); String id; if (slideAttachment instanceof DatabaseAttachment) { - id = ((DatabaseAttachment) slideAttachment).attachmentId.serialize(); + DatabaseAttachment dbAttachment = (DatabaseAttachment) slideAttachment; + id = dbAttachment.attachmentId.serialize(); + + if (SignalStore.internal().getShowArchiveStateHint()) { + View mediaArchive = findViewById(R.id.thumbnail_media_archive); + mediaArchive.setVisibility(View.VISIBLE); + switch (dbAttachment.archiveTransferState) { + case NONE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLACK)); + case COPY_PENDING -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE)); + case UPLOAD_IN_PROGRESS -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.CYAN)); + case FINISHED -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.GREEN)); + case TEMPORARY_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.YELLOW)); + case PERMANENT_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); + } + } } else { final Uri uri = slideAttachment.getUri(); if (uri != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt index e273b8308c..c2e735e302 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt @@ -248,6 +248,15 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter } ) + switchPref( + title = DSLSettingsText.from("Show archive status hint"), + summary = DSLSettingsText.from("Shows a color square based on archive status, green good, red bad."), + isChecked = state.showArchiveStateHint, + onClick = { + viewModel.setShowMediaArchiveStateHint(!state.showArchiveStateHint) + } + ) + clickPref( title = DSLSettingsText.from("Log dump PreKey ServiceId-KeyIds"), onClick = { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt index 88497c1d38..022b5bcb60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt @@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.emoji.EmojiFiles data class InternalSettingsState( val seeMoreUserDetails: Boolean, val shakeToReport: Boolean, + val showArchiveStateHint: Boolean, val gv2forceInvites: Boolean, val gv2ignoreP2PChanges: Boolean, val allowCensorshipSetting: Boolean, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt index bdd7e0fa53..a5f0fca2ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt @@ -44,6 +44,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito refresh() } + fun setShowMediaArchiveStateHint(enabled: Boolean) { + preferenceDataStore.putBoolean(InternalValues.SHOW_ARCHIVE_STATE_HINT, enabled) + refresh() + } + fun setDisableStorageService(enabled: Boolean) { preferenceDataStore.putBoolean(InternalValues.DISABLE_STORAGE_SERVICE, enabled) refresh() @@ -167,6 +172,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito private fun getState() = InternalSettingsState( seeMoreUserDetails = SignalStore.internal.recipientDetails, shakeToReport = SignalStore.internal.shakeToReport, + showArchiveStateHint = SignalStore.internal.showArchiveStateHint, gv2forceInvites = SignalStore.internal.gv2ForceInvites, gv2ignoreP2PChanges = SignalStore.internal.gv2IgnoreP2PChanges, allowCensorshipSetting = SignalStore.internal.allowChangingCensorshipSetting, diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.kt index f8822a2c5c..f8f84395f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.kt @@ -33,6 +33,7 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal const val NEW_CALL_UI: String = "internal.new.call.ui" const val LARGE_SCREEN_UI: String = "internal.large.screen.ui" const val FORCE_SPLIT_PANE_ON_COMPACT_LANDSCAPE: String = "internal.force.split.pane.on.compact.landscape.ui" + const val SHOW_ARCHIVE_STATE_HINT: String = "internal.show_archive_state_hint" } public override fun onFirstEverAppLaunch() = Unit @@ -178,6 +179,8 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal var forceSsre2Capability by booleanValue("internal.force_ssre2_capability", false).defaultForExternalUsers() + var showArchiveStateHint by booleanValue(SHOW_ARCHIVE_STATE_HINT, false).defaultForExternalUsers() + private fun SignalStoreValueDelegate.defaultForExternalUsers(): SignalStoreValueDelegate { return this.withPrecondition { RemoteConfig.internalUser } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java index f423b5d6e4..39c436635d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java @@ -17,6 +17,8 @@ package org.thoughtcrime.securesms.mediaoverview; import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -39,12 +41,14 @@ import org.signal.core.util.ByteSize; import org.signal.libsignal.protocol.util.Pair; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState; import org.thoughtcrime.securesms.database.MediaTable; import org.thoughtcrime.securesms.database.MediaTable.MediaRecord; import org.thoughtcrime.securesms.database.loaders.GroupedThreadMediaLoader.GroupedThreadMedia; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mediapreview.MediaPreviewCache; import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.Slide; @@ -520,6 +524,22 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { super.bind(context, mediaRecord, slide); documentType.setText(slide.getFileType(context).orElse("").toLowerCase()); + + if (SignalStore.internal().getShowArchiveStateHint() && slide.asAttachment() instanceof DatabaseAttachment) { + DatabaseAttachment dbAttachment = (DatabaseAttachment) slide.asAttachment(); + View mediaArchive = itemView.findViewById(R.id.thumbnail_media_archive); + if (mediaArchive != null) { + mediaArchive.setVisibility(View.VISIBLE); + switch (dbAttachment.archiveTransferState) { + case NONE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLACK)); + case COPY_PENDING -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE)); + case UPLOAD_IN_PROGRESS -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.CYAN)); + case FINISHED -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.GREEN)); + case TEMPORARY_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.YELLOW)); + case PERMANENT_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); + } + } + } } } @@ -552,6 +572,22 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(audioView, mediaRecord)); itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(audioView, mediaRecord)); + + if (SignalStore.internal().getShowArchiveStateHint() && slide.asAttachment() instanceof DatabaseAttachment) { + DatabaseAttachment dbAttachment = (DatabaseAttachment) slide.asAttachment(); + View mediaArchive = itemView.findViewById(R.id.thumbnail_media_archive); + if (mediaArchive != null) { + mediaArchive.setVisibility(View.VISIBLE); + switch (dbAttachment.archiveTransferState) { + case NONE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLACK)); + case COPY_PENDING -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE)); + case UPLOAD_IN_PROGRESS -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.CYAN)); + case FINISHED -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.GREEN)); + case TEMPORARY_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.YELLOW)); + case PERMANENT_FAILURE -> mediaArchive.setBackgroundTintList(ColorStateList.valueOf(Color.RED)); + } + } + } } @Override diff --git a/app/src/main/res/layout/audio_view.xml b/app/src/main/res/layout/audio_view.xml index 9faa96d45a..13fdbc380f 100644 --- a/app/src/main/res/layout/audio_view.xml +++ b/app/src/main/res/layout/audio_view.xml @@ -40,4 +40,12 @@ + \ No newline at end of file diff --git a/app/src/main/res/layout/document_view.xml b/app/src/main/res/layout/document_view.xml index 509809d713..d08083bc82 100644 --- a/app/src/main/res/layout/document_view.xml +++ b/app/src/main/res/layout/document_view.xml @@ -148,4 +148,13 @@ tools:text="24kb" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/media_overview_detail_item_audio.xml b/app/src/main/res/layout/media_overview_detail_item_audio.xml index 8d7142804c..4a3cecc2d7 100644 --- a/app/src/main/res/layout/media_overview_detail_item_audio.xml +++ b/app/src/main/res/layout/media_overview_detail_item_audio.xml @@ -26,6 +26,15 @@ android:scaleType="centerInside" app:srcCompat="@drawable/ic_audio_24" /> + + diff --git a/app/src/main/res/layout/media_overview_detail_item_document.xml b/app/src/main/res/layout/media_overview_detail_item_document.xml index 81d76658d1..1271e23655 100644 --- a/app/src/main/res/layout/media_overview_detail_item_document.xml +++ b/app/src/main/res/layout/media_overview_detail_item_document.xml @@ -37,6 +37,15 @@ tools:text="pdf" tools:visibility="visible" /> + + diff --git a/app/src/main/res/layout/thumbnail_view.xml b/app/src/main/res/layout/thumbnail_view.xml index 59c0568082..24ba59fd5a 100644 --- a/app/src/main/res/layout/thumbnail_view.xml +++ b/app/src/main/res/layout/thumbnail_view.xml @@ -82,4 +82,14 @@ android:background="@color/transparent" android:layout="@layout/transfer_controls_stub" /> + +