mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-04 15:35:38 +01:00
Add long-press context menu in all media screen.
This commit is contained in:
+8
-4
@@ -305,22 +305,26 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
|
||||
protected void updateSelectedView() {
|
||||
boolean selected = isSelected();
|
||||
itemView.setSelected(selected);
|
||||
if (selectedIndicator != null) {
|
||||
selectedIndicator.animate().cancel();
|
||||
selectedIndicator.setAlpha(isSelected() ? 1f : 0f);
|
||||
selectedIndicator.setAlpha(selected ? 1f : 0f);
|
||||
}
|
||||
}
|
||||
|
||||
protected void animateSelectedView() {
|
||||
boolean selected = isSelected();
|
||||
itemView.setSelected(selected);
|
||||
if (selectedIndicator != null) {
|
||||
selectedIndicator.animate()
|
||||
.alpha(isSelected() ? 1f : 0f)
|
||||
.alpha(selected ? 1f : 0f)
|
||||
.setDuration(SELECTION_ANIMATION_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
boolean onLongClick() {
|
||||
itemClickListener.onMediaLongClicked(mediaRecord);
|
||||
itemClickListener.onMediaLongClicked(itemView, mediaRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -817,7 +821,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
|
||||
interface ItemClickListener {
|
||||
void onMediaClicked(@NonNull View view, @NonNull MediaTable.MediaRecord mediaRecord);
|
||||
|
||||
void onMediaLongClicked(MediaTable.MediaRecord mediaRecord);
|
||||
void onMediaLongClicked(@NonNull View view, MediaTable.MediaRecord mediaRecord);
|
||||
}
|
||||
|
||||
interface AudioItemListener {
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package org.thoughtcrime.securesms.mediaoverview
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.signal.core.util.dp
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.database.MediaTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.signal.core.ui.R as CoreUiR
|
||||
|
||||
/**
|
||||
* Context menu shown when long-pressing a media item in [MediaOverviewPageFragment].
|
||||
*/
|
||||
class MediaOverviewContextMenu(
|
||||
private val fragment: Fragment,
|
||||
private val callbacks: Callbacks
|
||||
) {
|
||||
|
||||
private val lifecycleDisposable by lazy { LifecycleDisposable().bindTo(fragment.viewLifecycleOwner) }
|
||||
|
||||
fun show(anchor: View, mediaRecord: MediaTable.MediaRecord) {
|
||||
val recyclerView = anchor.parent as? RecyclerView
|
||||
recyclerView?.suppressLayout(true)
|
||||
anchor.isSelected = true
|
||||
|
||||
SignalContextMenu.Builder(anchor, anchor.parent as ViewGroup)
|
||||
.preferredVerticalPosition(SignalContextMenu.VerticalPosition.BELOW)
|
||||
.offsetY(4.dp)
|
||||
.onDismiss {
|
||||
anchor.isSelected = false
|
||||
recyclerView?.suppressLayout(false)
|
||||
}
|
||||
.show(
|
||||
listOfNotNull(
|
||||
getSaveActionItem(mediaRecord),
|
||||
getDeleteActionItem(mediaRecord),
|
||||
getSelectActionItem(mediaRecord),
|
||||
getJumpToMessageActionItem(mediaRecord)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getSaveActionItem(mediaRecord: MediaTable.MediaRecord): ActionItem? {
|
||||
if (mediaRecord.attachment == null) return null
|
||||
return ActionItem(
|
||||
iconRes = R.drawable.symbol_save_android_24,
|
||||
title = fragment.getString(R.string.save)
|
||||
) {
|
||||
callbacks.onSave(mediaRecord)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeleteActionItem(mediaRecord: MediaTable.MediaRecord): ActionItem {
|
||||
return ActionItem(
|
||||
iconRes = CoreUiR.drawable.symbol_trash_24,
|
||||
title = fragment.getString(R.string.delete)
|
||||
) {
|
||||
callbacks.onDelete(mediaRecord)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectActionItem(mediaRecord: MediaTable.MediaRecord): ActionItem {
|
||||
return ActionItem(
|
||||
iconRes = CoreUiR.drawable.symbol_check_circle_24,
|
||||
title = fragment.getString(R.string.CallContextMenu__select)
|
||||
) {
|
||||
callbacks.onSelect(mediaRecord)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getJumpToMessageActionItem(mediaRecord: MediaTable.MediaRecord): ActionItem {
|
||||
return ActionItem(
|
||||
iconRes = R.drawable.symbol_open_24,
|
||||
title = fragment.getString(R.string.MediaOverviewActivity_jump_to_message)
|
||||
) {
|
||||
lifecycleDisposable += Single.fromCallable<Int> {
|
||||
val dateReceived = SignalDatabase.messages.getMessageRecordOrNull(mediaRecord.messageId)?.dateReceived
|
||||
?: mediaRecord.date
|
||||
SignalDatabase.messages.getMessagePositionInConversation(mediaRecord.threadId, dateReceived)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy { position ->
|
||||
fragment.startActivity(
|
||||
ConversationIntents.createBuilderSync(fragment.requireContext(), mediaRecord.threadRecipientId, mediaRecord.threadId)
|
||||
.withStartingPosition(maxOf(0, position))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callbacks {
|
||||
fun onSave(mediaRecord: MediaTable.MediaRecord)
|
||||
fun onDelete(mediaRecord: MediaTable.MediaRecord)
|
||||
fun onSelect(mediaRecord: MediaTable.MediaRecord)
|
||||
}
|
||||
}
|
||||
+45
-4
@@ -63,6 +63,7 @@ import org.json.JSONException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
@@ -392,12 +393,52 @@ public final class MediaOverviewPageFragment extends LoggingFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaLongClicked(MediaTable.MediaRecord mediaRecord) {
|
||||
if (actionMode == null) {
|
||||
enterMultiSelect();
|
||||
public void onMediaLongClicked(@NonNull View view, MediaTable.MediaRecord mediaRecord) {
|
||||
if (actionMode != null) {
|
||||
handleMediaMultiSelectClick(mediaRecord);
|
||||
return;
|
||||
}
|
||||
|
||||
handleMediaMultiSelectClick(mediaRecord);
|
||||
new MediaOverviewContextMenu(this, new MediaOverviewContextMenu.Callbacks() {
|
||||
@Override
|
||||
public void onSave(@NonNull MediaTable.MediaRecord record) {
|
||||
handleSaveSingleMedia(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(@NonNull MediaTable.MediaRecord record) {
|
||||
handleDeleteSingleMedia(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelect(@NonNull MediaTable.MediaRecord record) {
|
||||
enterMultiSelect();
|
||||
handleMediaMultiSelectClick(record);
|
||||
}
|
||||
}).show(view, mediaRecord);
|
||||
}
|
||||
|
||||
private void handleSaveSingleMedia(@NonNull MediaTable.MediaRecord mediaRecord) {
|
||||
if (SignalStore.backup().getOptimizeStorage() && mediaRecord.getAttachment() != null && !mediaRecord.getAttachment().hasData) {
|
||||
OffloadedMediaDialogUtil.showAllOffloaded(requireContext());
|
||||
return;
|
||||
}
|
||||
lifecycleDisposable.add(
|
||||
MediaActions.handleSaveMedia(this, Collections.singleton(mediaRecord))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe()
|
||||
);
|
||||
}
|
||||
|
||||
private void handleDeleteSingleMedia(@NonNull MediaTable.MediaRecord mediaRecord) {
|
||||
if (DeleteSyncEducationDialog.shouldShow()) {
|
||||
lifecycleDisposable.add(
|
||||
DeleteSyncEducationDialog.show(getChildFragmentManager())
|
||||
.subscribe(() -> handleDeleteSingleMedia(mediaRecord))
|
||||
);
|
||||
return;
|
||||
}
|
||||
MediaActions.handleDeleteMedia(requireContext(), Collections.singleton(mediaRecord));
|
||||
}
|
||||
|
||||
private void handleDeleteSelectedMedia() {
|
||||
|
||||
Reference in New Issue
Block a user