diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java index 7239cd8277..71a03315c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -4,6 +4,7 @@ import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.drawable.ColorDrawable; +import android.hardware.Camera; import android.net.Uri; import android.text.SpannableString; import android.text.format.DateUtils; @@ -189,6 +190,8 @@ public class InputPanel extends LinearLayout stickerSuggestion.setAdapter(stickerSuggestionAdapter); editMessageCancel.setOnClickListener(v -> exitEditMessageMode()); + + quickCameraToggle.setVisibility(View.GONE); } public void setListener(final @NonNull Listener listener) { @@ -196,6 +199,13 @@ public class InputPanel extends LinearLayout mediaKeyboard.setOnClickListener(v -> listener.onEmojiToggle()); voiceNoteDraftView.setListener(listener); + + if (Camera.getNumberOfCameras() > 0) { + quickCameraToggle.setOnClickListener(v -> listener.onQuickCameraToggleClicked()); + quickCameraToggle.setVisibility(View.VISIBLE); + } else { + quickCameraToggle.setVisibility(View.GONE); + } } public void setMediaListener(@NonNull MediaListener listener) { @@ -730,6 +740,7 @@ public class InputPanel extends LinearLayout void onQuoteCleared(); void onEnterEditMode(); void onExitEditMode(); + void onQuickCameraToggleClicked(); } private static class SlideToCancel { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java index a5fcd56c9e..e5f1c59e9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java @@ -1976,13 +1976,6 @@ public class ConversationParentFragment extends Fragment composeText.setOnClickListener(composeKeyPressedListener); composeText.setOnFocusChangeListener(composeKeyPressedListener); - if (Camera.getNumberOfCameras() > 0) { - quickCameraToggle.setVisibility(View.VISIBLE); - quickCameraToggle.setOnClickListener(new QuickCameraToggleListener()); - } else { - quickCameraToggle.setVisibility(View.GONE); - } - searchNav.setEventListener(this); inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); @@ -3767,24 +3760,6 @@ public class ConversationParentFragment extends Fragment } } - private class QuickCameraToggleListener implements OnClickListener { - @Override - public void onClick(View v) { - Permissions.with(ConversationParentFragment.this) - .request(Manifest.permission.CAMERA) - .ifNecessary() - .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_camera_24) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) - .onAllGranted(() -> { - composeText.clearFocus(); - startActivityForResult(MediaSelectionActivity.camera(requireActivity(), sendButton.getSelectedSendType(), recipient.getId(), inputPanel.getQuote().isPresent()), MEDIA_SENDER); - requireActivity().overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); - }) - .onAnyDenied(() -> Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) - .execute(); - } - } - private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener { @Override public void onClick(View v) { @@ -4272,6 +4247,22 @@ public class ConversationParentFragment extends Fragment } } + @Override + public void onQuickCameraToggleClicked() { + Permissions.with(ConversationParentFragment.this) + .request(Manifest.permission.CAMERA) + .ifNecessary() + .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_camera_24) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted(() -> { + composeText.clearFocus(); + startActivityForResult(MediaSelectionActivity.camera(requireActivity(), sendButton.getSelectedSendType(), recipient.getId(), inputPanel.getQuote().isPresent()), MEDIA_SENDER); + requireActivity().overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); + }) + .onAnyDenied(() -> Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) + .execute(); + } + @Override public void onMessageActionToolbarOpened() { searchViewItem.collapseActionView(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt index 81628047db..05bba72eb1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.ContactsContract +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContract import androidx.core.content.IntentCompat import androidx.fragment.app.Fragment @@ -52,6 +53,7 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat private val mediaGalleryLauncher = fragment.registerForActivityResult(MediaGallery) { result -> callbacks.onMediaSend(result) } private val selectLocationLauncher = fragment.registerForActivityResult(SelectLocation) { result -> callbacks.onLocationSelected(result?.place, result?.uri) } private val selectFileLauncher = fragment.registerForActivityResult(SelectFile) { result -> callbacks.onFileSelected(result) } + private val cameraLauncher = fragment.registerForActivityResult(MediaCapture) { result -> callbacks.onMediaSend(result) } fun launchContactShareEditor(uri: Uri, chatColors: ChatColors) { contactShareLauncher.launch(uri to chatColors) @@ -77,6 +79,20 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat .execute() } + fun launchCamera(recipientId: RecipientId, isReply: Boolean) { + Permissions.with(fragment) + .request(Manifest.permission.CAMERA) + .ifNecessary() + .withRationaleDialog(fragment.getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24) + .withPermanentDenialDialog(fragment.getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted { + cameraLauncher.launch(MediaSelectionInput(emptyList(), recipientId, null, isReply)) + fragment.requireActivity().overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary) + } + .onAnyDenied { Toast.makeText(fragment.requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show() } + .execute() + } + fun launchMediaEditor(mediaList: List, recipientId: RecipientId, text: CharSequence?) { mediaSelectionLauncher.launch(MediaSelectionInput(mediaList, recipientId, text)) } @@ -131,6 +147,21 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat } } + private object MediaCapture : ActivityResultContract() { + override fun createIntent(context: Context, input: MediaSelectionInput): Intent { + val (_, recipientId, _, isReply) = input + return MediaSelectionActivity.camera(context, MessageSendType.SignalMessageSendType, recipientId, isReply) + } + + override fun parseResult(resultCode: Int, intent: Intent?): MediaSendActivityResult? { + return if (resultCode == Activity.RESULT_OK) { + intent?.let { MediaSendActivityResult.fromData(intent) } + } else { + null + } + } + } + private object MediaGallery : ActivityResultContract() { override fun createIntent(context: Context, input: MediaSelectionInput): Intent { val (media, recipientId, text, isReply) = input diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 36d7663438..4a3f15ec52 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -1205,6 +1205,7 @@ class ConversationFragment : composeText.setDraftText(data.draftText) inputPanel.enterEditMessageMode(GlideApp.with(this), data.messageEdit, true) } + is ShareOrDraftData.SetLocation -> attachmentManager.setLocation(data.location, MediaConstraints.getPushMediaConstraints()) is ShareOrDraftData.SetMedia -> { composeText.setDraftText(data.text) @@ -3395,6 +3396,12 @@ class ConversationFragment : previousPages = null } } + + override fun onQuickCameraToggleClicked() { + val recipientId = viewModel.recipientSnapshot?.id ?: return + composeText.clearFocus() + conversationActivityResultContracts.launchCamera(recipientId, inputPanel.quote.isPresent) + } } //endregion diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt index d4be6bf5af..e172516b2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt @@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.util.DateUtils -import org.thoughtcrime.securesms.util.LongClickMovementMethod import org.thoughtcrime.securesms.util.Projection import org.thoughtcrime.securesms.util.ProjectionList import org.thoughtcrime.securesms.util.SignalLocalMetrics