Update camera permission UI in media.
This commit is contained in:
parent
b14eddefc9
commit
c3c743fbb8
15 changed files with 286 additions and 81 deletions
|
@ -131,14 +131,15 @@ public class DeviceActivity extends PassphraseRequiredActivity
|
|||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code))
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_scan_qr_code_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_scan_qr_codes, getSupportFragmentManager())
|
||||
.onAllGranted(() -> {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.fragment_container, deviceAddFragment)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss();
|
||||
})
|
||||
.onAnyDenied(() -> Toast.makeText(this, R.string.DeviceActivity_unable_to_scan_a_qr_code_without_the_camera_permission, Toast.LENGTH_LONG).show())
|
||||
.onAnyDenied(() -> Toast.makeText(this, R.string.CameraXFragment_signal_needs_camera_access_scan_qr_code, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.components.recyclerview.GridDividerDecoration
|
|||
import org.thoughtcrime.securesms.groups.ParcelableGroupId
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.permissions.PermissionCompat
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
|
@ -222,18 +223,22 @@ class AvatarPickerFragment : Fragment(R.layout.avatar_picker_fragment) {
|
|||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun openCameraCapture() {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.onAllGranted {
|
||||
val intent = AvatarSelectionActivity.getIntentForCameraCapture(requireContext())
|
||||
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE)
|
||||
}
|
||||
.onAnyDenied {
|
||||
Toast.makeText(requireContext(), R.string.AvatarSelectionBottomSheetDialogFragment__taking_a_photo_requires_the_camera_permission, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
.execute()
|
||||
if (CameraXUtil.isSupported()) {
|
||||
val intent = AvatarSelectionActivity.getIntentForCameraCapture(requireContext())
|
||||
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE)
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.onAllGranted {
|
||||
val intent = AvatarSelectionActivity.getIntentForCameraCapture(requireContext())
|
||||
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE)
|
||||
}
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.AvatarSelectionBottomSheetDialogFragment__taking_a_photo_requires_the_camera_permission), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos, getParentFragmentManager())
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.AvatarSelectionBottomSheetDialogFragment__taking_a_photo_requires_the_camera_permission, Toast.LENGTH_SHORT).show() }
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
|
|
@ -78,6 +78,7 @@ import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupInviteSentD
|
|||
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupsLearnMoreBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
|
||||
import org.thoughtcrime.securesms.nicknames.NicknameActivity
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
|
@ -420,14 +421,16 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
|||
.setMessage(R.string.ConversationSettingsFragment__only_admins_of_this_group_can_add_to_its_story)
|
||||
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
|
||||
.show()
|
||||
} else if (CameraXUtil.isSupported()) {
|
||||
addToGroupStoryDelegate.addToStory(state.recipient.id)
|
||||
} else {
|
||||
Permissions.with(this@ConversationSettingsFragment)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.onAllGranted { addToGroupStoryDelegate.addToStory(state.recipient.id) }
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show() }
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.giph.ui.GiphyActivity
|
|||
import org.thoughtcrime.securesms.maps.PlacePickerActivity
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
@ -74,17 +75,22 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat
|
|||
}
|
||||
|
||||
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()
|
||||
if (CameraXUtil.isSupported()) {
|
||||
cameraLauncher.launch(MediaSelectionInput(emptyList(), recipientId, null, isReply))
|
||||
fragment.requireActivity().overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary)
|
||||
} else {
|
||||
Permissions.with(fragment)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(fragment.getString(R.string.CameraXFragment_allow_access_camera), fragment.getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(fragment.getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, fragment.parentFragmentManager)
|
||||
.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.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
fun launchMediaEditor(mediaList: List<Media>, recipientId: RecipientId, text: CharSequence?) {
|
||||
|
|
|
@ -144,6 +144,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|||
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity;
|
||||
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder;
|
||||
import org.thoughtcrime.securesms.main.SearchBinder;
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil;
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity;
|
||||
import org.thoughtcrime.securesms.megaphone.Megaphone;
|
||||
import org.thoughtcrime.securesms.megaphone.MegaphoneActionController;
|
||||
|
@ -392,14 +393,18 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
|
||||
fab.setOnClickListener(v -> startActivity(new Intent(getActivity(), NewConversationActivity.class)));
|
||||
cameraFab.setOnClickListener(v -> {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.onAllGranted(() -> startActivity(MediaSelectionActivity.camera(requireContext())))
|
||||
.onAnyDenied(() -> Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
if (CameraXUtil.isSupported()) {
|
||||
startActivity(MediaSelectionActivity.camera(requireContext()));
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.onAllGranted(() -> startActivity(MediaSelectionActivity.camera(requireContext())))
|
||||
.onAnyDenied(() -> Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
});
|
||||
|
||||
initializeViewModel();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.Animator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
|
@ -24,6 +24,8 @@ import android.view.animation.AnimationUtils;
|
|||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -37,6 +39,7 @@ import androidx.constraintlayout.widget.ConstraintSet;
|
|||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import org.signal.core.util.Stopwatch;
|
||||
|
@ -57,6 +60,8 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaAnimations;
|
|||
import org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.MemoryFileDescriptor;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
@ -65,6 +70,7 @@ import org.thoughtcrime.securesms.video.VideoUtil;
|
|||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
@ -72,6 +78,8 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import kotlin.Unit;
|
||||
|
||||
import static org.thoughtcrime.securesms.permissions.PermissionDeniedBottomSheet.showPermissionFragment;
|
||||
|
||||
/**
|
||||
* Camera captured implemented using the CameraX SDK, which uses Camera2 under the hood. Should be
|
||||
* preferred whenever possible.
|
||||
|
@ -98,6 +106,9 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
|||
private CameraXModePolicy cameraXModePolicy;
|
||||
private CameraScreenBrightnessController cameraScreenBrightnessController;
|
||||
private boolean isMediaSelected;
|
||||
private View missingPermissionsContainer;
|
||||
private TextView missingPermissionsText;
|
||||
private MaterialButton allowAccessButton;
|
||||
|
||||
private final Executor qrAnalysisExecutor = Executors.newSingleThreadExecutor();
|
||||
private final QrProcessor qrProcessor = new QrProcessor();
|
||||
|
@ -149,13 +160,18 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
|||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
this.cameraParent = view.findViewById(R.id.camerax_camera_parent);
|
||||
this.cameraParent = view.findViewById(R.id.camerax_camera_parent);
|
||||
|
||||
this.previewView = view.findViewById(R.id.camerax_camera);
|
||||
this.controlsContainer = view.findViewById(R.id.camerax_controls_container);
|
||||
this.cameraXModePolicy = CameraXModePolicy.acquire(requireContext(),
|
||||
controller.getMediaConstraints(),
|
||||
requireArguments().getBoolean(IS_VIDEO_ENABLED, true));
|
||||
this.previewView = view.findViewById(R.id.camerax_camera);
|
||||
this.controlsContainer = view.findViewById(R.id.camerax_controls_container);
|
||||
this.cameraXModePolicy = CameraXModePolicy.acquire(requireContext(),
|
||||
controller.getMediaConstraints(),
|
||||
requireArguments().getBoolean(IS_VIDEO_ENABLED, true));
|
||||
this.missingPermissionsContainer = view.findViewById(R.id.missing_permissions_container);
|
||||
this.missingPermissionsText = view.findViewById(R.id.missing_permissions_text);
|
||||
this.allowAccessButton = view.findViewById(R.id.allow_access_button);
|
||||
|
||||
checkPermissions(requireArguments().getBoolean(IS_VIDEO_ENABLED, true));
|
||||
|
||||
Log.d(TAG, "Starting CameraX with mode policy " + cameraXModePolicy.getClass().getSimpleName());
|
||||
|
||||
|
@ -218,6 +234,9 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
|||
|
||||
cameraController.bindToLifecycle(getViewLifecycleOwner(), () -> Log.d(TAG, "Camera init complete from onResume"));
|
||||
requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
if (hasCameraPermission()) {
|
||||
missingPermissionsContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -259,6 +278,61 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void checkPermissions(boolean includeAudio) {
|
||||
if (hasCameraPermission()) {
|
||||
missingPermissionsContainer.setVisibility(View.GONE);
|
||||
} else {
|
||||
boolean hasAudioPermission = Permissions.hasAll(requireContext(), Manifest.permission.RECORD_AUDIO);
|
||||
missingPermissionsContainer.setVisibility(View.VISIBLE);
|
||||
int textResId = (!includeAudio || hasAudioPermission) ? R.string.CameraXFragment_to_capture_photos_and_video_allow_camera : R.string.CameraXFragment_to_capture_photos_and_video_allow_camera_microphone;
|
||||
missingPermissionsText.setText(textResId);
|
||||
allowAccessButton.setOnClickListener(v -> requestPermissions(includeAudio));
|
||||
}
|
||||
}
|
||||
|
||||
private void requestPermissions(boolean includeAudio) {
|
||||
if (includeAudio) {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||||
.ifNecessary()
|
||||
.onSomeGranted(permissions -> {
|
||||
if (permissions.contains(Manifest.permission.CAMERA)) {
|
||||
missingPermissionsContainer.setVisibility(View.GONE);
|
||||
}
|
||||
})
|
||||
.onSomePermanentlyDenied(deniedPermissions -> {
|
||||
if (deniedPermissions.containsAll(List.of(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))) {
|
||||
showPermissionFragment(R.string.CameraXFragment_allow_access_camera_microphone, R.string.CameraXFragment_to_capture_photos_videos).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
|
||||
} else if (deniedPermissions.contains(Manifest.permission.CAMERA)) {
|
||||
showPermissionFragment(R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
|
||||
}
|
||||
})
|
||||
.onSomeDenied(deniedPermissions -> {
|
||||
if (deniedPermissions.contains(Manifest.permission.CAMERA)) {
|
||||
Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
})
|
||||
.execute();
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.onAllGranted (() -> missingPermissionsContainer.setVisibility(View.GONE))
|
||||
.onAnyDenied(() -> Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show())
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos, getParentFragmentManager())
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasCameraPermission() {
|
||||
return Permissions.hasAll(requireContext(), Manifest.permission.CAMERA);
|
||||
}
|
||||
|
||||
private void onOrientationChanged() {
|
||||
int layout = R.layout.camera_controls_portrait;
|
||||
|
||||
|
@ -356,7 +430,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
|||
selfieFlash = requireView().findViewById(R.id.camera_selfie_flash);
|
||||
|
||||
captureButton.setOnClickListener(v -> {
|
||||
if (cameraController.isInitialized()) {
|
||||
if (hasCameraPermission() && cameraController.isInitialized()) {
|
||||
captureButton.setEnabled(false);
|
||||
flipButton.setEnabled(false);
|
||||
flashButton.setEnabled(false);
|
||||
|
|
|
@ -118,13 +118,17 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
|
|||
public void onVideoCaptureStarted() {
|
||||
Log.d(TAG, "onVideoCaptureStarted");
|
||||
|
||||
if (canRecordAudio()) {
|
||||
if (canUseCamera() && canRecordAudio()) {
|
||||
beginCameraRecording();
|
||||
} else {
|
||||
} else if (!canRecordAudio()) {
|
||||
displayAudioRecordingPermissionsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canUseCamera() {
|
||||
return Permissions.hasAll(fragment.requireContext(), Manifest.permission.CAMERA);
|
||||
}
|
||||
|
||||
private boolean canRecordAudio() {
|
||||
return Permissions.hasAll(fragment.requireContext(), Manifest.permission.RECORD_AUDIO);
|
||||
}
|
||||
|
@ -133,9 +137,9 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
|
|||
Permissions.with(fragment)
|
||||
.request(Manifest.permission.RECORD_AUDIO)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(fragment.getString(R.string.ConversationActivity_enable_the_microphone_permission_to_capture_videos_with_sound), R.drawable.ic_mic_solid_24)
|
||||
.withPermanentDenialDialog(fragment.getString(R.string.ConversationActivity_signal_needs_the_recording_permissions_to_capture_video))
|
||||
.onAnyDenied(() -> Toast.makeText(fragment.requireContext(), R.string.ConversationActivity_signal_needs_recording_permissions_to_capture_video, Toast.LENGTH_LONG).show())
|
||||
.withRationaleDialog(fragment.getString(R.string.CameraXFragment_allow_access_microphone), fragment.getString(R.string.CameraXFragment_to_capture_videos_with_sound), R.drawable.ic_mic_24)
|
||||
.withPermanentDenialDialog(fragment.getString(R.string.ConversationActivity_signal_needs_the_recording_permissions_to_capture_video), null, R.string.CameraXFragment_allow_access_microphone, R.string.CameraXFragment_to_capture_videos, fragment.getParentFragmentManager())
|
||||
.onAnyDenied(() -> Toast.makeText(fragment.requireContext(), R.string.CameraXFragment_signal_needs_microphone_access_video, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.widget.Toast
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.permissions.PermissionCompat
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
@ -33,14 +34,18 @@ class MediaSelectionNavigator(
|
|||
fun Fragment.requestPermissionsForCamera(
|
||||
onGranted: () -> Unit
|
||||
) {
|
||||
Permissions.with(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(onGranted)
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
if (CameraXUtil.isSupported()) {
|
||||
onGranted()
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.ic_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.onAllGranted(onGranted)
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
fun Fragment.requestPermissionsForGallery(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.mediasend.v2.gallery
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
|
@ -19,6 +20,7 @@ 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.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.permissions.PermissionCompat
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
||||
|
@ -94,7 +96,18 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
|
|||
if (callbacks.isCameraEnabled()) {
|
||||
binding.mediaGalleryToolbar.setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.action_camera) {
|
||||
callbacks.onNavigateToCamera()
|
||||
if (CameraXUtil.isSupported()) {
|
||||
callbacks.onNavigateToCamera()
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.onAllGranted { callbacks.onNavigateToCamera() }
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.ic_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -96,22 +96,13 @@ public final class PaymentsTransferFragment extends LoggingFragment {
|
|||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.PaymentsTransferFragment__to_scan_a_qr_code_signal_needs), R.drawable.ic_camera_24)
|
||||
.onAnyPermanentlyDenied(this::onCameraPermissionPermanentlyDenied)
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.PaymentsTransferFragment__to_scan_a_qr_code_signal_needs), R.drawable.ic_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.PaymentsTransferFragment__to_scan_a_qr_code_signal_needs_access_to_the_camera), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_scan_qr_codes, getParentFragmentManager())
|
||||
.onAllGranted(() -> SafeNavigation.safeNavigate(Navigation.findNavController(requireView()), R.id.action_paymentsTransfer_to_paymentsScanQr))
|
||||
.onAnyDenied(() -> Toast.makeText(requireContext(), R.string.PaymentsTransferFragment__to_scan_a_qr_code_signal_needs_access_to_the_camera, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
|
||||
private void onCameraPermissionPermanentlyDenied() {
|
||||
new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.Permissions_permission_required)
|
||||
.setMessage(R.string.PaymentsTransferFragment__signal_needs_the_camera_permission_to_capture_qr_code_go_to_settings)
|
||||
.setPositiveButton(R.string.PaymentsTransferFragment__settings, (dialog, which) -> requireActivity().startActivity(Permissions.getApplicationSettingsIntent(requireContext())))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
|||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent
|
||||
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
|
||||
import org.thoughtcrime.securesms.main.SearchBinder
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity
|
||||
|
@ -224,16 +225,18 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
|||
})
|
||||
|
||||
cameraFab.setOnClickListener {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.onAllGranted {
|
||||
startActivityIfAble(MediaSelectionActivity.camera(requireContext(), isStory = true))
|
||||
}
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
if (CameraXUtil.isSupported()) {
|
||||
startActivityIfAble(MediaSelectionActivity.camera(requireContext(), isStory = true))
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.onAllGranted { startActivityIfAble(MediaSelectionActivity.camera(requireContext(), isStory = true)) }
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) {
|
||||
|
|
|
@ -106,7 +106,8 @@ class VerifyIdentityFragment : Fragment(R.layout.fragment_container), ScanListen
|
|||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied))
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_scan_qr_code_allow_camera), R.drawable.ic_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_scan_qr_codes, getParentFragmentManager())
|
||||
.onAllGranted {
|
||||
childFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_bottom, R.anim.slide_from_bottom, R.anim.slide_to_top)
|
||||
|
@ -114,7 +115,7 @@ class VerifyIdentityFragment : Fragment(R.layout.fragment_container), ScanListen
|
|||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.VerifyIdentityActivity_unable_to_scan_qr_code_without_camera_permission, Toast.LENGTH_LONG).show() }
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_scan_qr_code, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
}
|
||||
|
||||
|
|
23
app/src/main/res/drawable/permission_camera.xml
Normal file
23
app/src/main/res/drawable/permission_camera.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M30.389,7.5H41.611C43.292,7.5 44.897,8.206 46.034,9.446L50.438,14.25H55.5C61.299,14.25 66,18.951 66,24.75V52.5C66,58.299 61.299,63 55.5,63H16.5C10.701,63 6,58.299 6,52.5V24.75C6,18.951 10.701,14.25 16.5,14.25H21.563L25.966,9.446C27.103,8.206 28.708,7.5 30.389,7.5Z"
|
||||
android:fillColor="#C2D5F0"/>
|
||||
<path
|
||||
android:pathData="M66,33.093V52.5C66,58.299 61.299,63 55.5,63H16.5C10.701,63 6,58.299 6,52.5V33.093C14.581,27.35 24.899,24 36,24C47.101,24 57.419,27.35 66,33.093Z"
|
||||
android:fillColor="#AEC8E8"/>
|
||||
<path
|
||||
android:pathData="M24.861,8.432C26.281,6.882 28.287,6 30.389,6H41.611C43.713,6 45.719,6.882 47.139,8.432L51.097,12.75H55.5C62.127,12.75 67.5,18.123 67.5,24.75V52.5C67.5,59.127 62.127,64.5 55.5,64.5H16.5C9.873,64.5 4.5,59.127 4.5,52.5V24.75C4.5,18.123 9.873,12.75 16.5,12.75H20.903L24.861,8.432ZM30.389,9C29.128,9 27.925,9.529 27.072,10.459L22.668,15.264C22.384,15.573 21.983,15.75 21.563,15.75H16.5C11.529,15.75 7.5,19.779 7.5,24.75V52.5C7.5,57.471 11.529,61.5 16.5,61.5H55.5C60.471,61.5 64.5,57.471 64.5,52.5V24.75C64.5,19.779 60.471,15.75 55.5,15.75H50.438C50.017,15.75 49.616,15.573 49.332,15.264L44.928,10.459C44.075,9.529 42.872,9 41.611,9H30.389Z"
|
||||
android:fillColor="#6C7B9D"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36,36.75m-15,0a15,15 0,1 1,30 0a15,15 0,1 1,-30 0"
|
||||
android:fillColor="#DDE7FF"/>
|
||||
<path
|
||||
android:pathData="M36,23.25C28.544,23.25 22.5,29.294 22.5,36.75C22.5,44.206 28.544,50.25 36,50.25C43.456,50.25 49.5,44.206 49.5,36.75C49.5,29.294 43.456,23.25 36,23.25ZM19.5,36.75C19.5,27.637 26.887,20.25 36,20.25C45.113,20.25 52.5,27.637 52.5,36.75C52.5,45.863 45.113,53.25 36,53.25C26.887,53.25 19.5,45.863 19.5,36.75Z"
|
||||
android:fillColor="#6C7B9D"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -39,4 +39,43 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/camerax_camera_parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/missing_permissions_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="-50dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/permission_camera" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/missing_permissions_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:text="@string/CameraXFragment_to_capture_photos_and_video_allow_camera" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/allow_access_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:text="@string/CameraXFragment_allow_access" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -225,6 +225,38 @@
|
|||
<string name="CameraXFragment_capture_description">Capture</string>
|
||||
<string name="CameraXFragment_change_camera_description">Change camera</string>
|
||||
<string name="CameraXFragment_open_gallery_description">Open gallery</string>
|
||||
<!-- Button text asking for access to camera permissions -->
|
||||
<string name="CameraXFragment_allow_access">Allow access</string>
|
||||
<!-- Dialog title asking users for camera and microphone permission -->
|
||||
<string name="CameraXFragment_allow_access_camera_microphone">Allow access to your camera and microphone</string>
|
||||
<!-- Dialog title asking users for camera permission -->
|
||||
<string name="CameraXFragment_allow_access_camera">Allow access to your camera</string>
|
||||
<!-- Dialog title asking users for microphone permission -->
|
||||
<string name="CameraXFragment_allow_access_microphone">Allow access to your microphone</string>
|
||||
<!-- Text explaining why Signal needs camera access in order to take photos -->
|
||||
<string name="CameraXFragment_to_capture_photos_allow_camera">To capture photos, allow Signal access to the camera.</string>
|
||||
<!-- Text explaining why Signal needs camera access in order to take photos and videos -->
|
||||
<string name="CameraXFragment_to_capture_photos_and_video_allow_camera">To capture photos and video, allow Signal access to the camera.</string>
|
||||
<!-- Text explaining why Signal needs camera and microphone access in order to take photos and videos -->
|
||||
<string name="CameraXFragment_to_capture_photos_and_video_allow_camera_microphone">To capture photos and video, allow Signal access to the camera and microphone.</string>
|
||||
<!-- Text explaining why Signal needs microphone access to take videos -->
|
||||
<string name="CameraXFragment_to_capture_videos_with_sound">To capture videos with sound, allow Signal access to your microphone.</string>
|
||||
<!-- Text explaining why Signal needs camera access to scan QR codes -->
|
||||
<string name="CameraXFragment_to_scan_qr_code_allow_camera">To scan a QR code, allow Signal access to the camera.</string>
|
||||
<!-- Toast dialog explaining why Signal needs camera permissions when capturing photos -->
|
||||
<string name="CameraXFragment_signal_needs_camera_access_capture_photos">Signal needs camera access to capture photos</string>
|
||||
<!-- Toast dialog explaining why Signal needs camera permissions when scanning QR codes -->
|
||||
<string name="CameraXFragment_signal_needs_camera_access_scan_qr_code">Signal needs camera access to scan QR codes</string>
|
||||
<!-- Toast dialog explaining why Signal needs microphone permissions -->
|
||||
<string name="CameraXFragment_signal_needs_microphone_access_video">Signal needs microphone access to capture video</string>
|
||||
<!-- Dialog description that explains the steps needed to give camera permission -->
|
||||
<string name="CameraXFragment_to_capture_photos">To capture photos in Signal:</string>
|
||||
<!-- Dialog description that explains the steps needed to give camera and microphone permission -->
|
||||
<string name="CameraXFragment_to_capture_photos_videos">To capture photos and videos in Signal:</string>
|
||||
<!-- Dialog description that explains the steps needed to give microphone permission -->
|
||||
<string name="CameraXFragment_to_capture_videos">To capture videos with sound:</string>
|
||||
<!-- Dialog description that explains the steps needed to give Signal camera permissions -->
|
||||
<string name="CameraXFragment_to_scan_qr_codes">To scan QR codes:</string>
|
||||
|
||||
<!-- CameraContacts -->
|
||||
<string name="CameraContacts_recent_contacts">Recent contacts</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue