Merge pull request #4829 from nextcloud/feature/4313/addNewPhotoPicker

Add new photo picker
This commit is contained in:
Marcel Hibbe 2025-04-02 15:33:23 +00:00 committed by GitHub
commit 7a54e498e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 205 additions and 123 deletions

View File

@ -47,6 +47,8 @@ import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
@ -213,6 +215,7 @@ import java.util.concurrent.ExecutionException
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.set import kotlin.collections.set
import kotlin.math.roundToInt import kotlin.math.roundToInt
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class ChatActivity : class ChatActivity :
@ -334,6 +337,8 @@ class ChatActivity :
private var videoURI: Uri? = null private var videoURI: Uri? = null
private lateinit var pickMultipleMedia: ActivityResultLauncher<PickVisualMediaRequest>
private val onBackPressedCallback = object : OnBackPressedCallback(true) { private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java) val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java)
@ -429,6 +434,14 @@ class ChatActivity :
onBackPressedDispatcher.addCallback(this, onBackPressedCallback) onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
initObservers() initObservers()
pickMultipleMedia = registerForActivityResult(
ActivityResultContracts.PickMultipleVisualMedia(5)
) { uris ->
if (uris.isNotEmpty()) {
onChooseFileResult(uris)
}
}
} }
private fun getMessageInputFragment(): MessageInputFragment { private fun getMessageInputFragment(): MessageInputFragment {
@ -1943,33 +1956,47 @@ class ChatActivity :
} }
} }
@Throws(IllegalStateException::class)
private fun onChooseFileResult(intent: Intent?) { private fun onChooseFileResult(intent: Intent?) {
try { try {
checkNotNull(intent) checkNotNull(intent)
filesToUpload.clear() val fileUris = mutableListOf<Uri>()
intent.clipData?.let { intent.clipData?.let {
for (index in 0 until it.itemCount) { for (index in 0 until it.itemCount) {
filesToUpload.add(it.getItemAt(index).uri.toString()) fileUris.add(it.getItemAt(index).uri)
} }
} ?: run { } ?: run {
checkNotNull(intent.data) checkNotNull(intent.data)
intent.data.let { intent.data.let {
filesToUpload.add(intent.data.toString()) fileUris.add(intent.data!!)
} }
} }
onChooseFileResult(fileUris)
} catch (e: IllegalStateException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
).show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
private fun onChooseFileResult(filesToUpload: List<Uri>) {
try {
require(filesToUpload.isNotEmpty()) require(filesToUpload.isNotEmpty())
val filenamesWithLineBreaks = StringBuilder("\n") val filenamesWithLineBreaks = StringBuilder("\n")
for (file in filesToUpload) { for (file in filesToUpload) {
val filename = FileUtils.getFileName(file.toUri(), context) val filename = FileUtils.getFileName(file, context)
filenamesWithLineBreaks.append(filename).append("\n") filenamesWithLineBreaks.append(filename).append("\n")
} }
val newFragment = FileAttachmentPreviewFragment.newInstance( val newFragment = FileAttachmentPreviewFragment.newInstance(
filenamesWithLineBreaks.toString(), filenamesWithLineBreaks.toString(),
filesToUpload filesToUpload.map { it.toString() }.toMutableList()
) )
newFragment.setListener { files, caption -> newFragment.setListener { files, caption ->
uploadFiles(files, caption) uploadFiles(files, caption)
@ -2223,6 +2250,10 @@ class ChatActivity :
chatViewModel.uploadFile(fileUri, room, currentConversation?.displayName!!, metaData) chatViewModel.uploadFile(fileUri, room, currentConversation?.displayName!!, metaData)
} }
fun showGalleryPicker() {
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))
}
private fun showLocalFilePicker() { private fun showLocalFilePicker() {
val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "*/*" type = "*/*"

View File

@ -402,6 +402,11 @@ class MessageInputFragment : Fragment() {
AttachmentDialog(requireActivity(), requireActivity() as ChatActivity).show() AttachmentDialog(requireActivity(), requireActivity() as ChatActivity).show()
} }
binding.fragmentMessageInputView.attachmentButton.setOnLongClickListener {
chatActivity.showGalleryPicker()
true
}
binding.fragmentMessageInputView.button?.setOnClickListener { binding.fragmentMessageInputView.button?.setOnClickListener {
submitMessage(false) submitMessage(false)
} }

View File

@ -89,16 +89,6 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
override fun doWork(): Result { override fun doWork(): Result {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
if (!platformPermissionUtil.isFilesPermissionGranted()) {
Log.w(
TAG,
"Storage permission is not granted. As a developer please make sure you check for" +
"permissions via UploadAndShareFilesWorker.isStoragePermissionGranted() and " +
"UploadAndShareFilesWorker.requestStoragePermission() beforehand. If you already " +
"did but end up with this warning, the user most likely revoked the permission"
)
}
return try { return try {
currentUser = currentUserProvider.currentUser.blockingGet() currentUser = currentUserProvider.currentUser.blockingGet()
val sourceFile = inputData.getString(DEVICE_SOURCE_FILE) val sourceFile = inputData.getString(DEVICE_SOURCE_FILE)

View File

@ -20,8 +20,8 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.databinding.DialogAttachmentBinding import com.nextcloud.talk.databinding.DialogAttachmentBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.SpreedFeatures
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -92,6 +92,11 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) :
dismiss() dismiss()
} }
dialogAttachmentBinding.menuAttachFileFromGallery.setOnClickListener {
chatActivity.showGalleryPicker()
dismiss()
}
dialogAttachmentBinding.menuAttachFileFromLocal.setOnClickListener { dialogAttachmentBinding.menuAttachFileFromLocal.setOnClickListener {
chatActivity.sendSelectLocalFileIntent() chatActivity.sendSelectLocalFileIntent()
dismiss() dismiss()

View File

@ -17,7 +17,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.databinding.DialogFileAttachmentPreviewBinding import com.nextcloud.talk.databinding.DialogFileAttachmentPreviewBinding
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import javax.inject.Inject import javax.inject.Inject
@ -70,12 +69,8 @@ class FileAttachmentPreviewFragment : DialogFragment() {
} }
binding.buttonSend.setOnClickListener { binding.buttonSend.setOnClickListener {
if (permissionUtil.isFilesPermissionGranted()) {
val caption: String = binding.dialogFileAttachmentPreviewCaption.text.toString() val caption: String = binding.dialogFileAttachmentPreviewCaption.text.toString()
uploadFiles(filesList, caption) uploadFiles(filesList, caption)
} else {
UploadAndShareFilesWorker.requestStoragePermission(requireActivity())
}
dismiss() dismiss()
} }
} }

View File

@ -0,0 +1,15 @@
<!--
~ Nextcloud Talk - Android Client
~
~ SPDX-FileCopyrightText: 2025 Your Name <your@email.com>
~ SPDX-License-Identifier: GPL-3.0-or-later
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@android:color/white"
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z"/>
</vector>

View File

@ -31,105 +31,6 @@
android:textColor="@color/medium_emphasis_text" android:textColor="@color/medium_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" /> android:textSize="@dimen/bottom_sheet_text_size" />
<LinearLayout
android:id="@+id/menu_attach_poll"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_attach_poll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_bar_chart_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_attach_poll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_create_poll"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_attach_contact"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_share_contact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_person_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/shareContactText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_share_contact"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_share_location"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_share_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_location_on_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_share_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_share_location"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/menu_attach_picture_from_cam" android:id="@+id/menu_attach_picture_from_cam"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -196,6 +97,39 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/menu_attach_file_from_gallery"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_attach_file_from_gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/baseline_photo_library_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_attach_file_from_gallery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_gallery"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/menu_attach_file_from_local" android:id="@+id/menu_attach_file_from_local"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -262,4 +196,110 @@
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:background="@color/low_emphasis_text"/>
<LinearLayout
android:id="@+id/menu_attach_poll"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_attach_poll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_bar_chart_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_attach_poll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_create_poll"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_share_location"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_share_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_location_on_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_share_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_share_location"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_attach_contact"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_item_height"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_share_contact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_person_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/shareContactText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingStart="@dimen/standard_double_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/nc_share_contact"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -541,6 +541,7 @@ How to translate with transifex:
<string name="nc_upload_confirm_send_single">Send this file to %1$s?</string> <string name="nc_upload_confirm_send_single">Send this file to %1$s?</string>
<string name="nc_upload_in_progess">Uploading</string> <string name="nc_upload_in_progess">Uploading</string>
<string name="nc_upload_from_device">Upload from device</string> <string name="nc_upload_from_device">Upload from device</string>
<string name="nc_gallery">Gallery</string>
<string name="nc_upload_notification_text">%1$s to %2$s - %3$s\%%</string> <string name="nc_upload_notification_text">%1$s to %2$s - %3$s\%%</string>
<string name="nc_upload_failed_notification_title">Failure</string> <string name="nc_upload_failed_notification_title">Failure</string>