Update gallery permission UI

This commit is contained in:
mtang-signal
2024-05-01 16:38:26 -04:00
committed by Alex Hart
parent 3c380d35fd
commit c95b180728
10 changed files with 156 additions and 16 deletions

View File

@@ -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) {

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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 ->