mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-23 03:05:26 +00:00
Update gallery permission UI
This commit is contained in:
@@ -29,7 +29,6 @@ import org.thoughtcrime.securesms.maps.PlacePickerActivity
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
|
||||
import org.thoughtcrime.securesms.permissions.PermissionCompat
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
@@ -71,13 +70,7 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat
|
||||
}
|
||||
|
||||
fun launchGallery(recipientId: RecipientId, text: CharSequence?, isReply: Boolean) {
|
||||
Permissions
|
||||
.with(fragment)
|
||||
.request(*PermissionCompat.forImagesAndVideos())
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio))
|
||||
.onAllGranted { mediaGalleryLauncher.launch(MediaSelectionInput(emptyList(), recipientId, text, isReply)) }
|
||||
.execute()
|
||||
mediaGalleryLauncher.launch(MediaSelectionInput(emptyList(), recipientId, text, isReply))
|
||||
}
|
||||
|
||||
fun launchCamera(recipientId: RecipientId, isReply: Boolean) {
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.conversation.v2.keyboard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -94,8 +95,10 @@ class AttachmentKeyboardFragment : LoggingFragment(R.layout.attachment_keyboard_
|
||||
override fun onAttachmentPermissionsRequested() {
|
||||
Permissions.with(requireParentFragment())
|
||||
.request(*PermissionCompat.forImagesAndVideos())
|
||||
.ifNecessary()
|
||||
.onAllGranted { viewModel.refreshRecentMedia() }
|
||||
.withPermanentDenialDialog(getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio))
|
||||
.withPermanentDenialDialog(getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio), null, R.string.AttachmentManager_signal_allow_storage, R.string.AttachmentManager_signal_to_show_photos, parentFragmentManager)
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.AttachmentManager_signal_needs_storage_access, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.mediasend.v2.gallery
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
@@ -18,6 +19,8 @@ import org.thoughtcrime.securesms.components.recyclerview.GridDividerDecoration
|
||||
import org.thoughtcrime.securesms.databinding.V2MediaGalleryFragmentBinding
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaRepository
|
||||
import org.thoughtcrime.securesms.permissions.PermissionCompat
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
||||
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
@@ -39,6 +42,7 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
||||
private lateinit var callbacks: Callbacks
|
||||
|
||||
private var selectedMediaTouchHelper: ItemTouchHelper? = null
|
||||
private var shouldEnableScrolling: Boolean = true
|
||||
|
||||
private val galleryAdapter = MappingAdapter()
|
||||
private val selectedAdapter = MappingAdapter()
|
||||
@@ -65,6 +69,10 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
||||
height = ViewUtil.getStatusBarHeight(view)
|
||||
}
|
||||
|
||||
binding.mediaGalleryGrid.layoutManager = object : GridLayoutManager(requireContext(), 4) {
|
||||
override fun canScrollVertically() = shouldEnableScrolling
|
||||
}
|
||||
|
||||
(binding.mediaGalleryGrid.layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
val isFolder: Boolean = (binding.mediaGalleryGrid.adapter as MappingAdapter).getModel(position).map { it is MediaGallerySelectableItem.FolderModel }.orElse(false)
|
||||
@@ -143,6 +151,8 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
||||
binding.mediaGalleryToolbar.title = state.bucketTitle ?: requireContext().getString(R.string.AttachmentKeyboard_gallery)
|
||||
}
|
||||
|
||||
binding.mediaGalleryAllowAccess.setOnClickListener { requestRequiredPermissions() }
|
||||
|
||||
val galleryItemsWithSelection = LiveDataUtil.combineLatest(
|
||||
viewModel.state.map { it.items },
|
||||
viewStateLiveData.map { it.selectedMedia }
|
||||
@@ -157,12 +167,34 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
||||
}
|
||||
|
||||
galleryItemsWithSelection.observe(viewLifecycleOwner) {
|
||||
galleryAdapter.submitList(it)
|
||||
if (!Permissions.hasAll(requireContext(), *PermissionCompat.forImagesAndVideos())) {
|
||||
binding.mediaGalleryMissingPermissions.visibility = View.VISIBLE
|
||||
shouldEnableScrolling = false
|
||||
galleryAdapter.submitList((1..100).map { MediaGallerySelectableItem.PlaceholderModel() })
|
||||
} else {
|
||||
binding.mediaGalleryMissingPermissions.visibility = View.GONE
|
||||
shouldEnableScrolling = true
|
||||
galleryAdapter.submitList(it)
|
||||
}
|
||||
}
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)
|
||||
}
|
||||
|
||||
private fun refreshMediaGallery() {
|
||||
viewModel.refreshMediaGallery()
|
||||
}
|
||||
|
||||
private fun requestRequiredPermissions() {
|
||||
Permissions.with(requireParentFragment())
|
||||
.request(*PermissionCompat.forImagesAndVideos())
|
||||
.ifNecessary()
|
||||
.onAllGranted { refreshMediaGallery() }
|
||||
.withPermanentDenialDialog(getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio), null, R.string.AttachmentManager_signal_allow_storage, R.string.AttachmentManager_signal_to_show_photos, parentFragmentManager)
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.AttachmentManager_signal_needs_storage_access, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
|
||||
fun onBack() {
|
||||
if (viewModel.pop()) {
|
||||
onBackPressedCallback.isEnabled = false
|
||||
|
||||
@@ -43,6 +43,16 @@ object MediaGallerySelectableItem {
|
||||
) {
|
||||
mappingAdapter.registerFactory(FolderModel::class.java, LayoutFactory({ FolderViewHolder(it, onMediaFolderClicked) }, R.layout.v2_media_gallery_folder_item))
|
||||
mappingAdapter.registerFactory(FileModel::class.java, LayoutFactory({ FileViewHolder(it, onMediaClicked) }, if (isMultiselectEnabled) R.layout.v2_media_gallery_item else R.layout.v2_media_gallery_item_no_check))
|
||||
mappingAdapter.registerFactory(PlaceholderModel::class.java, LayoutFactory({ PlaceholderViewHolder(it) }, R.layout.v2_media_gallery_placeholder_item))
|
||||
}
|
||||
|
||||
class PlaceholderViewHolder(itemView: View) : BaseViewHolder<PlaceholderModel>(itemView) {
|
||||
override fun bind(model: PlaceholderModel) = Unit
|
||||
}
|
||||
|
||||
class PlaceholderModel : MappingModel<PlaceholderModel> {
|
||||
override fun areItemsTheSame(newItem: PlaceholderModel): Boolean = true
|
||||
override fun areContentsTheSame(newItem: PlaceholderModel): Boolean = true
|
||||
}
|
||||
|
||||
class FolderModel(val mediaFolder: MediaFolder) : MappingModel<FolderModel> {
|
||||
@@ -58,7 +68,7 @@ object MediaGallerySelectableItem {
|
||||
|
||||
abstract class BaseViewHolder<T : MappingModel<T>>(itemView: View) : MappingViewHolder<T>(itemView) {
|
||||
protected val imageView: ShapeableImageView = itemView.findViewById(R.id.media_gallery_image)
|
||||
protected val playOverlay: ImageView = itemView.findViewById(R.id.media_gallery_play_overlay)
|
||||
protected val playOverlay: ImageView? = itemView.findViewById(R.id.media_gallery_play_overlay)
|
||||
protected val checkView: TextView? = itemView.findViewById(R.id.media_gallery_check)
|
||||
protected val title: TextView? = itemView.findViewById(R.id.media_gallery_title)
|
||||
}
|
||||
@@ -69,7 +79,7 @@ object MediaGallerySelectableItem {
|
||||
.load(DecryptableStreamUriLoader.DecryptableUri(model.mediaFolder.thumbnailUri))
|
||||
.into(imageView)
|
||||
|
||||
playOverlay.visible = false
|
||||
playOverlay?.visible = false
|
||||
itemView.setOnClickListener { onMediaFolderClicked(model.mediaFolder) }
|
||||
title?.text = model.mediaFolder.title
|
||||
title?.visible = true
|
||||
@@ -105,7 +115,7 @@ object MediaGallerySelectableItem {
|
||||
checkView?.visible = model.isSelected
|
||||
checkView?.text = "${model.selectionOneBasedIndex}"
|
||||
itemView.setOnClickListener { onMediaClicked(model.media, model.isSelected) }
|
||||
playOverlay.visible = MediaUtil.isVideo(model.media.mimeType) && !model.media.isVideoGif
|
||||
playOverlay?.visible = MediaUtil.isVideo(model.media.mimeType) && !model.media.isVideoGif
|
||||
title?.visible = false
|
||||
|
||||
if (PAYLOAD_INDEX_CHANGED in payload) {
|
||||
|
||||
@@ -29,6 +29,10 @@ class MediaGalleryViewModel(bucketId: String?, bucketTitle: String?, private val
|
||||
loadItemsForBucket(mediaFolder.bucketId, mediaFolder.title)
|
||||
}
|
||||
|
||||
fun refreshMediaGallery() {
|
||||
loadItemsForBucket(null, null)
|
||||
}
|
||||
|
||||
private fun loadItemsForBucket(bucketId: String?, bucketTitle: String?) {
|
||||
if (bucketId == null) {
|
||||
repository.getFolders { folders ->
|
||||
|
||||
Reference in New Issue
Block a user