diff --git a/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.kt b/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.kt index a6a0818..17d0c55 100644 --- a/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.kt +++ b/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.kt @@ -61,8 +61,36 @@ class CameraPlugin : Plugin() { override fun load() { super.load() - legacyFlow = LegacyCameraFlow(this) - ionFlow = IonCameraFlow(this) + + val permissionHelper = PermissionHelper( + isPermissionDeclaredFn = { alias -> isPermissionDeclared(alias) }, + getPermissionStateFn = { alias -> getPermissionState(alias) }, + requestPermissionForAliasFn = { alias, call, callbackName -> + requestPermissionForAlias(alias, call, callbackName) + }, + requestPermissionForAliasesFn = { aliases, call, callbackName -> + requestPermissionForAliases(aliases, call, callbackName) + } + ) + + legacyFlow = LegacyCameraFlow( + context, + activity, + bridge, + appId, + permissionHelper, + LegacyCameraFlow.ActivityStarter { call, intent, callbackName -> + startActivityForResult(call, intent, callbackName) + } + ) + + ionFlow = IonCameraFlow( + context, + activity, + bridge, + appId, + permissionHelper + ) ionFlow.load() } @@ -261,16 +289,4 @@ class CameraPlugin : Plugin() { ionFlow.onDestroy() } - fun requestLegacyPermissionForAlias(alias: String, call: PluginCall, callbackName: String) { - requestPermissionForAlias(alias, call, callbackName) - } - - fun requestLegacyPermissionForAliases( - aliases: Array, - call: PluginCall, - callbackName: String - ) { - requestPermissionForAliases(aliases, call, callbackName) - } - } diff --git a/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraFlow.kt b/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraFlow.kt index 901bb6b..a762080 100644 --- a/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraFlow.kt +++ b/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraFlow.kt @@ -13,9 +13,11 @@ import android.provider.MediaStore import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity import androidx.core.content.FileProvider import com.capacitorjs.plugins.camera.IonCameraSettings.Companion.DEFAULT_CORRECT_ORIENTATION import com.capacitorjs.plugins.camera.IonCameraSettings.Companion.DEFAULT_QUALITY +import com.getcapacitor.Bridge import com.getcapacitor.FileUtils import com.getcapacitor.JSArray import com.getcapacitor.JSObject @@ -41,7 +43,11 @@ import kotlinx.coroutines.launch import java.io.File class IonCameraFlow( - private val plugin: CameraPlugin + private val context: Context, + private val activity: AppCompatActivity, + private val bridge: Bridge, + private val appId: String, + private val permissionHelper: PermissionHelper ) { private var isFirstRequest = true private var cameraManager: IONCAMRCameraManager? = null @@ -70,7 +76,7 @@ class IonCameraFlow( fun load() { setupLaunchers() cameraManager = IONCAMRCameraManager( - plugin.getAppId(), + appId, IONCAMRExifHelper(), IONCAMRFileHelper(), IONCAMRMediaHelper(), @@ -89,14 +95,14 @@ class IonCameraFlow( ) editManager = IONCAMREditManager( - plugin.getAppId(), + appId, IONCAMRExifHelper(), IONCAMRFileHelper(), IONCAMRMediaHelper(), IONCAMRImageHelper() ) - cameraManager?.deleteVideoFilesFromCache(plugin.activity) + cameraManager?.deleteVideoFilesFromCache(activity) } fun takePhoto(call: PluginCall) { @@ -136,37 +142,37 @@ class IonCameraFlow( // Launchers // ---------------------------------------------------- private fun setupLaunchers() { - cameraLauncher = plugin.activity.registerForActivityResult( + cameraLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleCameraResult(result) } - cameraCropLauncher = plugin.activity.registerForActivityResult( + cameraCropLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleCameraCropResult(result) } - videoLauncher = plugin.activity.registerForActivityResult( + videoLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleVideoResult(result) } - galleryLauncher = plugin.activity.registerForActivityResult( + galleryLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleGalleryResult(result) } - galleryCropLauncher = plugin.activity.registerForActivityResult( + galleryCropLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleGalleryCropResult(result) } - editLauncher = plugin.activity.registerForActivityResult( + editLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> handleEditResult(result) @@ -227,7 +233,7 @@ class IonCameraFlow( private fun showCamera(call: PluginCall) { - if (!plugin.getContext().getPackageManager() + if (!context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) ) { sendError(IONCAMRError.NO_CAMERA_AVAILABLE_ERROR) @@ -249,7 +255,7 @@ class IonCameraFlow( return } currentCall = call - manager.takePhoto(plugin.getActivity(), settings.encodingType, cameraLauncher) + manager.takePhoto(activity, settings.encodingType, cameraLauncher) } catch (ex: Exception) { sendError(IONCAMRError.FAILED_TO_CAPTURE_IMAGE_ERROR) } @@ -270,7 +276,7 @@ class IonCameraFlow( } currentCall = call manager.recordVideo( - plugin.getActivity(), + activity, settings.saveToGallery, videoLauncher ) { @@ -291,7 +297,7 @@ class IonCameraFlow( val videoUri = call.getString("uri") ?: return sendError(IONCAMRError.PLAY_VIDEO_GENERAL_ERROR) - manager.playVideo(plugin.activity, videoUri, { + manager.playVideo(activity, videoUri, { call.resolve() }, { sendError(it) @@ -314,7 +320,7 @@ class IonCameraFlow( } manager.chooseFromGallery( - plugin.activity, + activity, settings.mediaType, settings.allowMultipleSelection, settings.limit, @@ -336,7 +342,7 @@ class IonCameraFlow( ) val imageBase64 = call.getString("inputImage") if (imageBase64 == null) return - manager.editImage(plugin.activity, imageBase64, editLauncher) + manager.editImage(activity, imageBase64, editLauncher) } private fun callEditURIPhoto(call: PluginCall) { @@ -357,7 +363,7 @@ class IonCameraFlow( includeMetadata = includeMetadata ) - manager.editURIPicture(plugin.activity, photoPath, editLauncher) { + manager.editURIPicture(activity, photoPath, editLauncher) { sendError(IONCAMRError.EDIT_IMAGE_ERROR) } } @@ -378,14 +384,14 @@ class IonCameraFlow( return } - val appId = plugin.getAppId() + val appId = appId val tmpFile = FileProvider.getUriForFile( - plugin.activity, + activity, "$appId$AUTHORITY", editor.createCaptureFile( - plugin.activity, + activity, settings.encodingType, - plugin.activity.getSharedPreferences( + activity.getSharedPreferences( CameraPlugin.STORE, Context.MODE_PRIVATE ).getString(CameraPlugin.EDIT_FILE_NAME_KEY, "") ?: "" @@ -453,13 +459,13 @@ class IonCameraFlow( val originalUri = uris.first() if (settings.editInApp) { editor.openCropActivity( - plugin.activity, + activity, originalUri, galleryCropLauncher ) } else { val tempUri = if (originalUri.scheme == "content") { - IonCameraUtils.getGalleryTempImage(plugin.activity, originalUri) + IonCameraUtils.getGalleryTempImage(activity, originalUri) } else { originalUri } @@ -474,7 +480,7 @@ class IonCameraFlow( galleryCropLauncher.launch(editIntent) } else { editor.openCropActivity( - plugin.activity, + activity, originalUri, galleryCropLauncher ) @@ -542,7 +548,7 @@ class IonCameraFlow( CoroutineScope(Dispatchers.Default).launch { manager.onChooseFromGalleryEditResult( - plugin.activity, + activity, Activity.RESULT_OK, intent, settings.includeMetadata, @@ -571,14 +577,14 @@ class IonCameraFlow( return } - val appId = plugin.getAppId() + val appId = appId val tmpFile = FileProvider.getUriForFile( - plugin.activity, + activity, "$appId$AUTHORITY", editor.createCaptureFile( - plugin.activity, + activity, settings.encodingType, - plugin.activity.getSharedPreferences( + activity.getSharedPreferences( CameraPlugin.STORE, Context.MODE_PRIVATE ).getString(CameraPlugin.EDIT_FILE_NAME_KEY, "") ?: "" @@ -586,7 +592,7 @@ class IonCameraFlow( ) editor.openCropActivity( - plugin.activity, + activity, tmpFile, cameraCropLauncher ) @@ -599,17 +605,17 @@ class IonCameraFlow( if (origPhotoUri.scheme == "file") { val editFile = File(origPhotoUri.path!!) editUri = FileProvider.getUriForFile( - plugin.activity, - plugin.context.packageName + AUTHORITY, + activity, + context.packageName + AUTHORITY, editFile ) lastEditUri = editFile.absolutePath } else if (origPhotoUri.scheme == "content") { - val tempUri = IonCameraUtils.getCameraTempImage(plugin.activity, origPhotoUri) ?: return null + val tempUri = IonCameraUtils.getCameraTempImage(activity, origPhotoUri) ?: return null val editFile = File(tempUri.path!!) editUri = FileProvider.getUriForFile( - plugin.activity, - plugin.context.packageName + AUTHORITY, + activity, + context.packageName + AUTHORITY, editFile ) lastEditUri = editFile.absolutePath @@ -624,8 +630,7 @@ class IonCameraFlow( val resInfoList: MutableList? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - resInfoList = plugin - .context + resInfoList = context .packageManager .queryIntentActivities(editIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())) } else { @@ -634,7 +639,7 @@ class IonCameraFlow( for (resolveInfo in resInfoList) { val packageName = resolveInfo.activityInfo.packageName - plugin.context.grantUriPermission(packageName, editUri, flags) + context.grantUriPermission(packageName, editUri, flags) } editIntent @@ -645,7 +650,7 @@ class IonCameraFlow( @Suppress("deprecation") private fun legacyQueryIntentActivities(intent: Intent): MutableList { - return plugin.context.packageManager + return context.packageManager .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) } @@ -704,12 +709,12 @@ class IonCameraFlow( return } - val exif = ImageUtils.getExifData(plugin.context, bitmap, uri) + val exif = ImageUtils.getExifData(context, bitmap, uri) val ret = JSObject() ret.put("type", mediaResult.type) ret.put("uri", mediaResult.uri) ret.put("thumbnail", mediaResult.thumbnail) - ret.put("webPath", FileUtils.getPortablePath(plugin.context, plugin.bridge.localUrl, uri)) + ret.put("webPath", FileUtils.getPortablePath(context, bridge.localUrl, uri)) ret.put("saved", mediaResult.saved) val metadata = JSObject() @@ -737,7 +742,7 @@ class IonCameraFlow( ret.put("type", mediaResult.type) ret.put("uri", mediaResult.uri) ret.put("thumbnail", mediaResult.thumbnail) - ret.put("webPath", FileUtils.getPortablePath(plugin.context, plugin.bridge.localUrl, uri)) + ret.put("webPath", FileUtils.getPortablePath(context, bridge.localUrl, uri)) ret.put("saved", mediaResult.saved) val metadata = JSObject() @@ -768,7 +773,7 @@ class IonCameraFlow( ret.put("saved", mediaResult.saved) ret.put( "webPath", - FileUtils.getPortablePath(plugin.context, plugin.bridge.localUrl, uri) + FileUtils.getPortablePath(context, bridge.localUrl, uri) ) val metadata = JSObject() @@ -787,7 +792,7 @@ class IonCameraFlow( return } - val exif = ImageUtils.getExifData(plugin.context, bitmap, uri) + val exif = ImageUtils.getExifData(context, bitmap, uri) metadata.put("exif", exif.toJson()) } @@ -814,7 +819,7 @@ class IonCameraFlow( } val ionParams = settings.toIonParameters() manager.processResultFromCamera( - plugin.activity, + activity, intent, ionParams, { image -> @@ -837,11 +842,11 @@ class IonCameraFlow( var uri = result.data?.data if (uri == null) { val fromPreferences = - plugin.activity.getSharedPreferences(CameraPlugin.STORE, Context.MODE_PRIVATE) + activity.getSharedPreferences(CameraPlugin.STORE, Context.MODE_PRIVATE) .getString(CameraPlugin.STORE, "") fromPreferences.let { uri = Uri.parse(fromPreferences) } } - if (plugin.activity == null) { + if (activity == null) { sendError(IONCAMRError.CAPTURE_VIDEO_ERROR) return } @@ -852,7 +857,7 @@ class IonCameraFlow( CoroutineScope(Dispatchers.Default).launch { manager.processResultFromVideo( - plugin.activity, + activity, uri, settings.saveToGallery, settings.isPersistent, @@ -879,7 +884,7 @@ class IonCameraFlow( CoroutineScope(Dispatchers.Default).launch { manager.onChooseFromGalleryResult( - plugin.activity, + activity, result.resultCode, result.data, settings.includeMetadata, @@ -897,7 +902,7 @@ class IonCameraFlow( } manager.processResultFromEdit( - plugin.activity, + activity, result.data, editParameters, { image -> @@ -929,18 +934,18 @@ class IonCameraFlow( fun checkCameraPermissions(call: PluginCall, saveToGallery: Boolean): Boolean { // if the manifest does not contain the camera permissions key, we don't need to ask the user - val needCameraPerms = plugin.isPermissionDeclared(CameraPlugin.CAMERA) + val needCameraPerms = permissionHelper.isPermissionDeclared(CameraPlugin.CAMERA) val hasCameraPerms = - !needCameraPerms || plugin.getPermissionState(CameraPlugin.CAMERA) == PermissionState.GRANTED + !needCameraPerms || permissionHelper.getPermissionState(CameraPlugin.CAMERA) == PermissionState.GRANTED val hasGalleryPerms = - plugin.getPermissionState(CameraPlugin.SAVE_GALLERY) == PermissionState.GRANTED + permissionHelper.getPermissionState(CameraPlugin.SAVE_GALLERY) == PermissionState.GRANTED // If we want to save to the gallery, we need two permissions // actually we only need permissions to save to gallery for Android <= 9 (API 28) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // we might still need to request permission for the camera if (!hasCameraPerms) { - plugin.requestLegacyPermissionForAlias( + permissionHelper.requestPermissionForAlias( CameraPlugin.CAMERA, call, "ionCameraPermissionsCallback" @@ -958,10 +963,10 @@ class IonCameraFlow( } else { arrayOf(CameraPlugin.SAVE_GALLERY) } - plugin.requestLegacyPermissionForAliases(aliases, call, "ionCameraPermissionsCallback") + permissionHelper.requestPermissionForAliases(aliases, call, "ionCameraPermissionsCallback") return false } else if (!hasCameraPerms) { - plugin.requestLegacyPermissionForAlias( + permissionHelper.requestPermissionForAlias( CameraPlugin.CAMERA, call, "ionCameraPermissionsCallback" @@ -972,7 +977,7 @@ class IonCameraFlow( } fun handleCameraPermissionsCallback(call: PluginCall) { - if (plugin.getPermissionState(CameraPlugin.CAMERA) != PermissionState.GRANTED) { + if (permissionHelper.getPermissionState(CameraPlugin.CAMERA) != PermissionState.GRANTED) { sendError(IONCAMRError.CAMERA_PERMISSION_DENIED_ERROR) return } @@ -1006,6 +1011,6 @@ class IonCameraFlow( } fun onDestroy() { - cameraManager?.deleteVideoFilesFromCache(plugin.activity) + cameraManager?.deleteVideoFilesFromCache(activity) } } \ No newline at end of file diff --git a/android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java b/android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java index 6976d99..77fafeb 100644 --- a/android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java +++ b/android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java @@ -4,6 +4,7 @@ import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -23,7 +24,9 @@ import androidx.activity.result.PickVisualMediaRequest; import androidx.activity.result.contract.ActivityResultContract; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.FileProvider; +import com.getcapacitor.Bridge; import com.getcapacitor.FileUtils; import com.getcapacitor.JSArray; import com.getcapacitor.JSObject; @@ -47,7 +50,19 @@ public class LegacyCameraFlow { - private final CameraPlugin plugin; + /** + * Functional interface for starting activities for result. + */ + public interface ActivityStarter { + void startActivityForResult(PluginCall call, Intent intent, String callbackName); + } + + private final Context context; + private final AppCompatActivity activity; + private final Bridge bridge; + private final String appId; + private final PermissionHelper permissionHelper; + private final ActivityStarter activityStarter; // Message constants private static final String INVALID_RESULT_TYPE_ERROR = "Invalid resultType option"; @@ -78,8 +93,20 @@ public class LegacyCameraFlow { private LegacyCameraSettings settings = new LegacyCameraSettings(); - public LegacyCameraFlow(CameraPlugin plugin) { - this.plugin = plugin; + public LegacyCameraFlow( + Context context, + AppCompatActivity activity, + Bridge bridge, + String appId, + PermissionHelper permissionHelper, + ActivityStarter activityStarter + ) { + this.context = context; + this.activity = activity; + this.bridge = bridge; + this.appId = appId; + this.permissionHelper = permissionHelper; + this.activityStarter = activityStarter; } public void getPhoto(PluginCall call) { @@ -136,11 +163,11 @@ private void showPrompt(final PluginCall call) { }, () -> call.reject(USER_CANCELLED) ); - fragment.show(plugin.getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet"); + fragment.show(activity.getSupportFragmentManager(), "capacitorModalsActionSheet"); } private void showCamera(final PluginCall call) { - if (!plugin.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) { + if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) { call.reject(NO_CAMERA_ERROR); return; } @@ -153,16 +180,16 @@ private void showPhotos(final PluginCall call) { public boolean checkCameraPermissions(PluginCall call) { // if the manifest does not contain the camera permissions key, we don't need to ask the user - boolean needCameraPerms = plugin.isPermissionDeclared(CameraPlugin.CAMERA); - boolean hasCameraPerms = !needCameraPerms || plugin.getPermissionState(CameraPlugin.CAMERA) == PermissionState.GRANTED; - boolean hasGalleryPerms = plugin.getPermissionState(CameraPlugin.SAVE_GALLERY) == PermissionState.GRANTED; + boolean needCameraPerms = permissionHelper.isPermissionDeclared(CameraPlugin.CAMERA); + boolean hasCameraPerms = !needCameraPerms || permissionHelper.getPermissionState(CameraPlugin.CAMERA) == PermissionState.GRANTED; + boolean hasGalleryPerms = permissionHelper.getPermissionState(CameraPlugin.SAVE_GALLERY) == PermissionState.GRANTED; // If we want to save to the gallery, we need two permissions // actually we only need permissions to save to gallery for Android <= 9 (API 28) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // we might still need to request permission for the camera if (!hasCameraPerms) { - plugin.requestLegacyPermissionForAlias(CameraPlugin.CAMERA, call, "cameraPermissionsCallback"); + permissionHelper.requestPermissionForAlias(CameraPlugin.CAMERA, call, "cameraPermissionsCallback"); return false; } return true; @@ -177,12 +204,12 @@ public boolean checkCameraPermissions(PluginCall call) { } else { aliases = new String[] { CameraPlugin.SAVE_GALLERY }; } - plugin.requestLegacyPermissionForAliases(aliases, call, "cameraPermissionsCallback"); + permissionHelper.requestPermissionForAliases(aliases, call, "cameraPermissionsCallback"); return false; } // If we don't need to save to the gallery, we can just ask for camera permissions else if (!hasCameraPerms) { - plugin.requestLegacyPermissionForAlias(CameraPlugin.CAMERA, call, "cameraPermissionsCallback"); + permissionHelper.requestPermissionForAlias(CameraPlugin.CAMERA, call, "cameraPermissionsCallback"); return false; } return true; @@ -192,8 +219,8 @@ public void handleCameraPermissionsCallback(PluginCall call) { if (call.getMethodName().equals("pickImages")) { openPhotos(call, true); } else { - if (settings.getSource() == CameraSource.CAMERA && plugin.getPermissionState(CameraPlugin.CAMERA) != PermissionState.GRANTED) { - Logger.debug(LOG_TAG, "User denied camera permission: " + plugin.getPermissionState(CameraPlugin.CAMERA)); + if (settings.getSource() == CameraSource.CAMERA && permissionHelper.getPermissionState(CameraPlugin.CAMERA) != PermissionState.GRANTED) { + Logger.debug(LOG_TAG, "User denied camera permission: " + permissionHelper.getPermissionState(CameraPlugin.CAMERA)); call.reject(PERMISSION_DENIED_ERROR_CAMERA); return; } @@ -234,21 +261,21 @@ private CameraResultType getResultType(String resultType) { public void openCamera(final PluginCall call) { if (checkCameraPermissions(call)) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(plugin.getContext().getPackageManager()) != null) { + if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) { // If we will be saving the photo, send the target file along try { - String appId = plugin.getAppId(); - File photoFile = CameraUtils.createImageFile(plugin.getActivity()); + String appId = this.appId; + File photoFile = CameraUtils.createImageFile(activity); imageFileSavePath = photoFile.getAbsolutePath(); // TODO: Verify provider config exists - imageFileUri = FileProvider.getUriForFile(plugin.getActivity(), appId + ".fileprovider", photoFile); + imageFileUri = FileProvider.getUriForFile(activity, appId + ".fileprovider", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri); } catch (Exception ex) { call.reject(IMAGE_FILE_SAVE_ERROR, ex); return; } - plugin.startActivityForResult(call, takePictureIntent, "processCameraImage"); + activityStarter.startActivityForResult(call, takePictureIntent, "processCameraImage"); } else { call.reject(NO_CAMERA_ACTIVITY_ERROR); } @@ -264,14 +291,14 @@ private ActivityResultLauncher registerActivityResultLauncher( ActivityResultCallback callback ) { String key = "cap_activity_rq#" + mNextLocalRequestCode.getAndIncrement(); - if (plugin.getBridge().getFragment() != null) { - Object host = plugin.getBridge().getFragment().getHost(); + if (bridge.getFragment() != null) { + Object host = bridge.getFragment().getHost(); if (host instanceof ActivityResultRegistryOwner) { return ((ActivityResultRegistryOwner) host).getActivityResultRegistry().register(key, contract, callback); } - return plugin.getBridge().getFragment().requireActivity().getActivityResultRegistry().register(key, contract, callback); + return bridge.getFragment().requireActivity().getActivityResultRegistry().register(key, contract, callback); } - return plugin.getBridge().getActivity().getActivityResultRegistry().register(key, contract, callback); + return bridge.getActivity().getActivityResultRegistry().register(key, contract, callback); } private ActivityResultContract> getContractForCall(final PluginCall call) { @@ -387,7 +414,7 @@ private void processPickedImage(Uri imageUri, PluginCall call) { InputStream imageStream = null; try { - imageStream = plugin.getContext().getContentResolver().openInputStream(imageUri); + imageStream = context.getContentResolver().openInputStream(imageUri); Bitmap bitmap = BitmapFactory.decodeStream(imageStream); if (bitmap == null) { @@ -415,7 +442,7 @@ private JSObject processPickedImages(Uri imageUri) { InputStream imageStream = null; JSObject ret = new JSObject(); try { - imageStream = plugin.getContext().getContentResolver().openInputStream(imageUri); + imageStream = context.getContentResolver().openInputStream(imageUri); Bitmap bitmap = BitmapFactory.decodeStream(imageStream); if (bitmap == null) { @@ -423,7 +450,7 @@ private JSObject processPickedImages(Uri imageUri) { return ret; } - ExifWrapper exif = ImageUtils.getExifData(plugin.getContext(), bitmap, imageUri); + ExifWrapper exif = ImageUtils.getExifData(context, bitmap, imageUri); try { bitmap = prepareBitmap(bitmap, imageUri, exif); } catch (IOException e) { @@ -440,7 +467,7 @@ private JSObject processPickedImages(Uri imageUri) { ret.put("format", "jpeg"); ret.put("exif", exif.toJson()); ret.put("path", newUri.toString()); - ret.put("webPath", FileUtils.getPortablePath(plugin.getContext(), plugin.getBridge().getLocalUrl(), newUri)); + ret.put("webPath", FileUtils.getPortablePath(context, bridge.getLocalUrl(), newUri)); } else { ret.put("error", UNABLE_TO_PROCESS_IMAGE); } @@ -518,7 +545,7 @@ private File getTempFile(Uri uri) { if (!filename.contains(".jpg") && !filename.contains(".jpeg")) { filename += "." + (new java.util.Date()).getTime() + ".jpeg"; } - File cacheDir = plugin.getContext().getCacheDir(); + File cacheDir = context.getCacheDir(); return new File(cacheDir, filename); } @@ -530,7 +557,7 @@ private File getTempFile(Uri uri) { */ @SuppressWarnings("deprecation") private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { - ExifWrapper exif = ImageUtils.getExifData(plugin.getContext(), bitmap, u); + ExifWrapper exif = ImageUtils.getExifData(context, bitmap, u); try { bitmap = prepareBitmap(bitmap, u, exif); } catch (IOException e) { @@ -554,7 +581,7 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { File fileToSave = new File(fileToSavePath); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ContentResolver resolver = plugin.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileToSave.getName()); values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg"); @@ -579,7 +606,7 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { } } else { String inserted = MediaStore.Images.Media.insertImage( - plugin.getContext().getContentResolver(), + context.getContentResolver(), fileToSavePath, fileToSave.getName(), "" @@ -634,7 +661,7 @@ private void returnFileURI(PluginCall call, ExifWrapper exif, Bitmap bitmap, Uri ret.put("format", "jpeg"); ret.put("exif", exif.toJson()); ret.put("path", newUri.toString()); - ret.put("webPath", FileUtils.getPortablePath(plugin.getContext(), plugin.getBridge().getLocalUrl(), newUri)); + ret.put("webPath", FileUtils.getPortablePath(context, bridge.getLocalUrl(), newUri)); ret.put("saved", isSaved); call.resolve(ret); } else { @@ -671,7 +698,7 @@ private Uri getTempImage(Uri u, ByteArrayOutputStream bitmapOutputStream) { */ private Bitmap prepareBitmap(Bitmap bitmap, Uri imageUri, ExifWrapper exif) throws IOException { if (settings.getShouldCorrectOrientation()) { - final Bitmap newBitmap = ImageUtils.correctOrientation(plugin.getContext(), bitmap, imageUri, exif); + final Bitmap newBitmap = ImageUtils.correctOrientation(context, bitmap, imageUri, exif); bitmap = replaceBitmap(bitmap, newBitmap); } @@ -718,7 +745,7 @@ private void editImage(PluginCall call, Uri uri, ByteArrayOutputStream bitmapOut Uri tempImage = getTempImage(uri, bitmapOutputStream); Intent editIntent = createEditIntent(tempImage); if (editIntent != null) { - plugin.startActivityForResult(call, editIntent, "processEditedImage"); + activityStarter.startActivityForResult(call, editIntent, "processEditedImage"); } else { call.reject(IMAGE_EDIT_ERROR); } @@ -731,8 +758,8 @@ private Intent createEditIntent(Uri origPhotoUri) { try { File editFile = new File(origPhotoUri.getPath()); Uri editUri = FileProvider.getUriForFile( - plugin.getActivity(), - plugin.getContext().getPackageName() + ".fileprovider", + activity, + context.getPackageName() + ".fileprovider", editFile ); Intent editIntent = new Intent(Intent.ACTION_EDIT); @@ -745,8 +772,7 @@ private Intent createEditIntent(Uri origPhotoUri) { List resInfoList; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - resInfoList = plugin - .getContext() + resInfoList = context .getPackageManager() .queryIntentActivities(editIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY)); } else { @@ -755,7 +781,7 @@ private Intent createEditIntent(Uri origPhotoUri) { for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; - plugin.getContext().grantUriPermission(packageName, editUri, flags); + context.grantUriPermission(packageName, editUri, flags); } return editIntent; } catch (Exception ex) { @@ -765,7 +791,7 @@ private Intent createEditIntent(Uri origPhotoUri) { @SuppressWarnings("deprecation") private List legacyQueryIntentActivities(Intent intent) { - return plugin.getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); } public void onSaveInstanceState(Bundle bundle) { diff --git a/android/src/main/java/com/capacitorjs/plugins/camera/PermissionHelper.kt b/android/src/main/java/com/capacitorjs/plugins/camera/PermissionHelper.kt new file mode 100644 index 0000000..1fd2eb8 --- /dev/null +++ b/android/src/main/java/com/capacitorjs/plugins/camera/PermissionHelper.kt @@ -0,0 +1,39 @@ +package com.capacitorjs.plugins.camera + +import com.getcapacitor.PermissionState +import com.getcapacitor.PluginCall + +/** + * Helper class for wrapping permission-related functionality. + */ +class PermissionHelper( + private val isPermissionDeclaredFn: (String) -> Boolean, + private val getPermissionStateFn: (String) -> PermissionState, + private val requestPermissionForAliasFn: (String, PluginCall, String) -> Unit, + private val requestPermissionForAliasesFn: (Array, PluginCall, String) -> Unit +) { + + fun isPermissionDeclared(alias: String): Boolean { + return isPermissionDeclaredFn(alias) + } + + fun getPermissionState(alias: String): PermissionState { + return getPermissionStateFn(alias) + } + + fun requestPermissionForAlias( + alias: String, + call: PluginCall, + callbackName: String + ) { + requestPermissionForAliasFn(alias, call, callbackName) + } + + fun requestPermissionForAliases( + aliases: Array, + call: PluginCall, + callbackName: String + ) { + requestPermissionForAliasesFn(aliases, call, callbackName) + } +}