kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add support for drag + drop in the media send flow.
rodzic
1dbb6013cb
commit
ddad9acef1
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
|||
import org.thoughtcrime.securesms.scribbles.ImageEditorFragment
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* ViewModel which maintains the list of selected media and other shared values.
|
||||
|
@ -62,6 +63,8 @@ class MediaSelectionViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastMediaDrag: Pair<Int, Int> = Pair(0, 0)
|
||||
|
||||
init {
|
||||
val recipientId = destination.getRecipientId()
|
||||
if (recipientId != null) {
|
||||
|
@ -123,6 +126,52 @@ class MediaSelectionViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun swapMedia(originalStart: Int, end: Int): Boolean {
|
||||
var start: Int = originalStart
|
||||
|
||||
if (lastMediaDrag.first == start && lastMediaDrag.second == end) {
|
||||
return true
|
||||
} else if (lastMediaDrag.first == start) {
|
||||
start = lastMediaDrag.second
|
||||
}
|
||||
|
||||
val snapshot = store.state
|
||||
|
||||
if (end >= snapshot.selectedMedia.size || end < 0 || start >= snapshot.selectedMedia.size || start < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
lastMediaDrag = Pair(originalStart, end)
|
||||
|
||||
val newMediaList = snapshot.selectedMedia.toMutableList()
|
||||
|
||||
if (start < end) {
|
||||
for (i in start until end) {
|
||||
Collections.swap(newMediaList, i, i + 1)
|
||||
}
|
||||
} else {
|
||||
for (i in start downTo end + 1) {
|
||||
Collections.swap(newMediaList, i, i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
store.update {
|
||||
it.copy(
|
||||
selectedMedia = newMediaList
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun isValidMediaDragPosition(position: Int): Boolean {
|
||||
return position >= 0 && position < store.state.selectedMedia.size
|
||||
}
|
||||
|
||||
fun onMediaDragFinished() {
|
||||
lastMediaDrag = Pair(0, 0)
|
||||
}
|
||||
|
||||
fun removeMedia(media: Media) {
|
||||
val snapshot = store.state
|
||||
val newMediaList = snapshot.selectedMedia - media
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.fragment.app.viewModels
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.recyclerview.GridDividerDecoration
|
||||
|
@ -39,6 +40,8 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
|||
private lateinit var bottomBarGroup: View
|
||||
private lateinit var selectedRecycler: RecyclerView
|
||||
|
||||
private var selectedMediaTouchHelper: ItemTouchHelper? = null
|
||||
|
||||
private val galleryAdapter = MappingAdapter()
|
||||
private val selectedAdapter = MappingAdapter()
|
||||
|
||||
|
@ -88,6 +91,7 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
|||
callbacks.onSelectedMediaClicked(media)
|
||||
}
|
||||
selectedRecycler.adapter = selectedAdapter
|
||||
selectedMediaTouchHelper?.attachToRecyclerView(selectedRecycler)
|
||||
|
||||
MediaGallerySelectableItem.registerAdapter(
|
||||
mappingAdapter = galleryAdapter,
|
||||
|
@ -153,6 +157,10 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
|||
viewStateLiveData.value = state
|
||||
}
|
||||
|
||||
fun bindSelectedMediaItemDragHelper(helper: ItemTouchHelper) {
|
||||
selectedMediaTouchHelper = helper
|
||||
}
|
||||
|
||||
data class ViewState(
|
||||
val selectedMedia: List<Media> = listOf()
|
||||
)
|
||||
|
|
|
@ -5,11 +5,13 @@ import android.view.View
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionNavigator
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionNavigator.Companion.requestPermissionsForCamera
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel
|
||||
import org.thoughtcrime.securesms.mediasend.v2.review.MediaSelectionItemTouchHelper
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
|
||||
private const val MEDIA_GALLERY_TAG = "MEDIA_GALLERY"
|
||||
|
@ -29,6 +31,8 @@ class MediaSelectionGalleryFragment : Fragment(R.layout.fragment_container), Med
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
mediaGalleryFragment = ensureMediaGalleryFragment()
|
||||
|
||||
mediaGalleryFragment.bindSelectedMediaItemDragHelper(ItemTouchHelper(MediaSelectionItemTouchHelper(sharedViewModel)))
|
||||
|
||||
sharedViewModel.state.observe(viewLifecycleOwner) { state ->
|
||||
mediaGalleryFragment.onViewStateUpdated(MediaGalleryFragment.ViewState(state.selectedMedia))
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.core.graphics.drawable.DrawableCompat
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
|
@ -181,6 +182,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
|||
}
|
||||
}
|
||||
selectionRecycler.adapter = selectionAdapter
|
||||
ItemTouchHelper(MediaSelectionItemTouchHelper(sharedViewModel)).attachToRecyclerView(selectionRecycler)
|
||||
|
||||
sharedViewModel.state.observe(viewLifecycleOwner) { state ->
|
||||
pagerAdapter.submitMedia(state.selectedMedia)
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.thoughtcrime.securesms.mediasend.v2.review;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel;
|
||||
|
||||
/**
|
||||
* A touch helper for handling drag + drop on the media rail in the media send flow.
|
||||
*/
|
||||
public class MediaSelectionItemTouchHelper extends ItemTouchHelper.Callback {
|
||||
|
||||
private final MediaSelectionViewModel viewModel;
|
||||
|
||||
public MediaSelectionItemTouchHelper(MediaSelectionViewModel viewModel) {
|
||||
this.viewModel = viewModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
if (viewModel.isValidMediaDragPosition(viewHolder.getAdapterPosition())) {
|
||||
int dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
|
||||
return makeMovementFlags(dragFlags, 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||
return viewModel.swapMedia(viewHolder.getAdapterPosition(), target.getAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
viewModel.onMediaDragFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.RecipientDatabase.InsightsBannerTier;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
|
@ -32,7 +34,6 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.MentionSetting;
|
|||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
|
@ -59,12 +60,9 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.RecipientDatabase.InsightsBannerTier;
|
||||
|
||||
public class Recipient {
|
||||
|
||||
private static final String TAG = Log.tag(Recipient.class);
|
||||
|
|
|
@ -55,12 +55,13 @@
|
|||
android:id="@+id/selection_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:alpha="0"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@id/controls_shade"
|
||||
tools:alpha="1"
|
||||
|
|
Ładowanie…
Reference in New Issue