diff --git a/cropper/src/main/java/com/canhub/cropper/CropImage.kt b/cropper/src/main/java/com/canhub/cropper/CropImage.kt index c35c37d1..029c3f49 100644 --- a/cropper/src/main/java/com/canhub/cropper/CropImage.kt +++ b/cropper/src/main/java/com/canhub/cropper/CropImage.kt @@ -387,6 +387,7 @@ object CropImage { * activity/fragment/widget. * @param data the returned data of the activity result */ + @JvmStatic fun getPickImageResultUriContent(context: Context, data: Intent?): Uri { var isCamera = true if (data != null && data.data != null) { @@ -406,6 +407,7 @@ object CropImage { * @param uniqueName If true, make each image cropped have a different file name, this could cause * memory issues, use wisely. [Default: false] */ + @JvmStatic fun getPickImageResultUriFilePath( context: Context, data: Intent?, @@ -485,6 +487,7 @@ object CropImage { * @return Crop Image Activity Result object or null if none exists */ // TODO don't return null + @JvmStatic fun getActivityResult(data: Intent?): ActivityResult? = data?.getParcelableExtra(CROP_IMAGE_EXTRA_RESULT) as? ActivityResult? diff --git a/sample/src/main/java/com/canhub/cropper/sample/SMainActivity.kt b/sample/src/main/java/com/canhub/cropper/sample/SMainActivity.kt index 94ad4886..aaaa550e 100644 --- a/sample/src/main/java/com/canhub/cropper/sample/SMainActivity.kt +++ b/sample/src/main/java/com/canhub/cropper/sample/SMainActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.canhub.cropper.sample.camera.app.SCameraFragment +import com.canhub.cropper.sample.camera_java.app.SCameraFragmentJava import com.canhub.cropper.sample.crop_image_view.app.SCropImageViewFragment import com.canhub.cropper.sample.extend_activity.app.SExtendActivity import com.example.croppersample.R @@ -36,6 +37,14 @@ internal class SMainActivity : AppCompatActivity() { .replace(R.id.container, SCameraFragment.newInstance()) .commit() } + + binding.sampleCropImageJava.setOnClickListener { + hideButtons(binding) + supportFragmentManager + .beginTransaction() + .replace(R.id.container, SCameraFragmentJava.newInstance()) + .commit() + } } private fun hideButtons(binding: ActivityMainBinding) { diff --git a/sample/src/main/java/com/canhub/cropper/sample/camera_java/app/SCameraFragmentJava.java b/sample/src/main/java/com/canhub/cropper/sample/camera_java/app/SCameraFragmentJava.java new file mode 100644 index 00000000..a128b33e --- /dev/null +++ b/sample/src/main/java/com/canhub/cropper/sample/camera_java/app/SCameraFragmentJava.java @@ -0,0 +1,286 @@ +package com.canhub.cropper.sample.camera_java.app; + +import android.Manifest; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; +import androidx.fragment.app.Fragment; + +import com.canhub.cropper.CropImage; +import com.canhub.cropper.CropImageView; +import com.canhub.cropper.sample.SCropResultActivity; +import com.canhub.cropper.sample.camera_java.domain.CameraEnumDomainJava; +import com.canhub.cropper.sample.camera_java.domain.SCameraContractJava; +import com.canhub.cropper.sample.camera_java.presenter.SCameraPresenterJava; +import com.example.croppersample.R; +import com.example.croppersample.databinding.FragmentCameraBinding; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Locale; + +import static android.graphics.Color.RED; +import static android.graphics.Color.WHITE; + +public class SCameraFragmentJava extends Fragment implements SCameraContractJava.View { + + public static final int CODE_PHOTO_CAMERA = 811917; + static final String DATE_FORMAT = "yyyyMMdd_HHmmss"; + static final String FILE_NAMING_PREFIX = "JPEG_"; + static final String FILE_NAMING_SUFFIX = "_"; + static final String FILE_FORMAT = ".jpg"; + static final String AUTHORITY_SUFFIX = ".fileprovider"; + public static final int CUSTOM_REQUEST_CODE = 8119153; + + private FragmentCameraBinding binding; + private final SCameraContractJava.Presenter presenter = new SCameraPresenterJava(); + private Uri photoUri; + private final ActivityResultLauncher requestPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), presenter::onPermissionResult); + + public static SCameraFragmentJava newInstance() { + return new SCameraFragmentJava(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + binding = FragmentCameraBinding.inflate(inflater, container, false); + + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + presenter.bind(this); + + binding.startWithUri.setOnClickListener(v -> presenter.startWithUriClicked()); + + binding.startWithoutUri.setOnClickListener(v -> presenter.startWithoutUriClicked()); + + binding.startPickImageActivity.setOnClickListener(v -> presenter.startPickImageActivityClicked()); + + binding.startActivityForResult.setOnClickListener(v -> presenter.startActivityForResultClicked()); + + presenter.onCreate(getActivity(), getContext()); + } + + @Override + public void startCropImage(@NotNull CameraEnumDomainJava option) { + switch (option) { + case START_WITH_URI: + startCameraWithUri(); + break; + case START_WITHOUT_URI: + startCameraWithoutUri(); + break; + case START_PICK_IMG: + startPickImage(); + break; + case START_FOR_RESULT: + startForResult(); + break; + default: + break; + } + } + + private void startForResult() { + assert (getContext() != null); + Intent intent = new Intent(); + intent.setType("image/*"); + intent.setAction(Intent.ACTION_GET_CONTENT); + startActivityForResult(Intent.createChooser(intent, "Select Picture"), CUSTOM_REQUEST_CODE); + + } + + private void startPickImage() { + assert (getContext() != null); + CropImage.activity() + .start(getContext(), this); + } + + private void startCameraWithoutUri() { + assert (getContext() != null); + Context ctx = getContext(); + CropImage.activity() + .setScaleType(CropImageView.ScaleType.CENTER) + .setCropShape(CropImageView.CropShape.OVAL) + .setGuidelines(CropImageView.Guidelines.ON) + .setAspectRatio(4, 16) + .setMaxZoom(8) + .setAutoZoomEnabled(false) + .setMultiTouchEnabled(false) + .setCenterMoveEnabled(true) + .setShowCropOverlay(false) + .setAllowFlipping(false) + .setSnapRadius(10f) + .setTouchRadius(30f) + .setInitialCropWindowPaddingRatio(0.3f) + .setBorderLineThickness(5f) + .setBorderLineColor(R.color.black) + .setBorderCornerThickness(6f) + .setBorderCornerOffset(2f) + .setBorderCornerLength(20f) + .setBorderCornerColor(RED) + .setGuidelinesThickness(5f) + .setGuidelinesColor(RED) + .setBackgroundColor(Color.argb(119, 30, 60, 90)) + .setMinCropWindowSize(20, 20) + .setMinCropResultSize(16, 16) + .setMaxCropResultSize(999, 999) + .setActivityTitle("CUSTOM title") + .setActivityMenuIconColor(RED) + .setOutputUri(null) + .setOutputCompressFormat(Bitmap.CompressFormat.PNG) + .setOutputCompressQuality(50) + .setRequestedSize(100, 100) + .setRequestedSize(100, 100, CropImageView.RequestSizeOptions.RESIZE_FIT) + .setInitialCropWindowRectangle(null) + .setInitialRotation(180) + .setAllowCounterRotation(true) + .setFlipHorizontally(true) + .setFlipVertically(true) + .setCropMenuCropButtonTitle("Custom name") + .setCropMenuCropButtonIcon(R.drawable.ic_gear_24) + .setAllowRotation(false) + .setNoOutputImage(false) + .setFixAspectRatio(true) + .start(ctx, this); + } + + private void startCameraWithUri() { + assert (getContext() != null); + Context ctx = getContext(); + CropImage.activity(photoUri) + .setScaleType(CropImageView.ScaleType.FIT_CENTER) + .setCropShape(CropImageView.CropShape.RECTANGLE) + .setGuidelines(CropImageView.Guidelines.ON_TOUCH) + .setAspectRatio(1, 1) + .setMaxZoom(4) + .setAutoZoomEnabled(true) + .setMultiTouchEnabled(true) + .setCenterMoveEnabled(true) + .setShowCropOverlay(true) + .setAllowFlipping(true) + .setSnapRadius(3f) + .setTouchRadius(48f) + .setInitialCropWindowPaddingRatio(0.1f) + .setBorderLineThickness(3f) + .setBorderLineColor(Color.argb(170, 255, 255, 255)) + .setBorderCornerThickness(2f) + .setBorderCornerOffset(5f) + .setBorderCornerLength(14f) + .setBorderCornerColor(WHITE) + .setGuidelinesThickness(1f) + .setGuidelinesColor(R.color.white) + .setBackgroundColor(Color.argb(119, 0, 0, 0)) + .setMinCropWindowSize(24, 24) + .setMinCropResultSize(20, 20) + .setMaxCropResultSize(99999, 99999) + .setActivityTitle("") + .setActivityMenuIconColor(0) + .setOutputUri(null) + .setOutputCompressFormat(Bitmap.CompressFormat.JPEG) + .setOutputCompressQuality(90) + .setRequestedSize(0, 0) + .setRequestedSize(0, 0, CropImageView.RequestSizeOptions.RESIZE_INSIDE) + .setInitialCropWindowRectangle(null) + .setInitialRotation(90) + .setAllowCounterRotation(false) + .setFlipHorizontally(false) + .setFlipVertically(false) + .setCropMenuCropButtonTitle(null) + .setCropMenuCropButtonIcon(0) + .setAllowRotation(true) + .setNoOutputImage(false) + .setFixAspectRatio(false) + .start(ctx, this); + } + + @Override + public void showErrorMessage(@NotNull String message) { + Log.e("Camera Error:", message); + Toast.makeText(getActivity(), "Crop failed: " + message, Toast.LENGTH_SHORT).show(); + } + + @Override + public void dispatchTakePictureIntent() { + assert (getContext() != null); + Context ctx = getContext(); + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + try { + if (takePictureIntent.resolveActivity(ctx.getPackageManager()) != null) { + String authorities = getContext().getPackageName() + AUTHORITY_SUFFIX; + photoUri = FileProvider.getUriForFile(ctx, authorities, createImageFile()); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + startActivityForResult(takePictureIntent, CODE_PHOTO_CAMERA); + } + } catch (ActivityNotFoundException e) { + // display error state to the user + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void cameraPermissionLaunch() { + requestPermissionLauncher.launch(Manifest.permission.CAMERA); + } + + @Override + public void showDialog() { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext()); + alertDialogBuilder.setTitle(R.string.missing_camera_permission_title); + alertDialogBuilder.setMessage(R.string.missing_camera_permission_body); + alertDialogBuilder.setPositiveButton(R.string.ok, (arg0, arg1) -> presenter.onOk()); + alertDialogBuilder.setNegativeButton(R.string.cancel, (dialog, which) -> presenter.onCancel()); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + + } + + @Override + public void handleCropImageResult(@NotNull String uri) { + SCropResultActivity.Companion.start(this, null, Uri.parse(uri), null); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + presenter.onActivityResult(resultCode, requestCode, data); + } + + private File createImageFile() throws IOException { + assert getActivity() != null; + SimpleDateFormat timeStamp = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault()); + File storageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES); + return File.createTempFile( + FILE_NAMING_PREFIX + FILE_NAMING_SUFFIX, + FILE_FORMAT, + storageDir + ); + } +} diff --git a/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/CameraEnumDomainJava.java b/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/CameraEnumDomainJava.java new file mode 100644 index 00000000..fab991c5 --- /dev/null +++ b/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/CameraEnumDomainJava.java @@ -0,0 +1,5 @@ +package com.canhub.cropper.sample.camera_java.domain; + +public enum CameraEnumDomainJava { + START_WITH_URI, START_WITHOUT_URI, START_PICK_IMG, START_FOR_RESULT; +} diff --git a/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/SCameraContractJava.java b/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/SCameraContractJava.java new file mode 100644 index 00000000..dc5b19fb --- /dev/null +++ b/sample/src/main/java/com/canhub/cropper/sample/camera_java/domain/SCameraContractJava.java @@ -0,0 +1,47 @@ +package com.canhub.cropper.sample.camera_java.domain; + +import android.content.Context; +import android.content.Intent; + +import androidx.fragment.app.FragmentActivity; + +public interface SCameraContractJava { + + interface View { + void startCropImage(CameraEnumDomainJava option); + + void showErrorMessage(String message); + + void dispatchTakePictureIntent(); + + void cameraPermissionLaunch(); + + void showDialog(); + + void handleCropImageResult(String uri); + } + + interface Presenter { + void bind(View view); + + void unbind(); + + void onPermissionResult(boolean granted); + + void onCreate(FragmentActivity activity, Context context); + + void onOk(); + + void onCancel(); + + void onActivityResult(int resultCode, int requestCode, Intent data); + + void startWithUriClicked(); + + void startWithoutUriClicked(); + + void startPickImageActivityClicked(); + + void startActivityForResultClicked(); + } +} diff --git a/sample/src/main/java/com/canhub/cropper/sample/camera_java/presenter/SCameraPresenterJava.java b/sample/src/main/java/com/canhub/cropper/sample/camera_java/presenter/SCameraPresenterJava.java new file mode 100644 index 00000000..ef4cb8b9 --- /dev/null +++ b/sample/src/main/java/com/canhub/cropper/sample/camera_java/presenter/SCameraPresenterJava.java @@ -0,0 +1,170 @@ +package com.canhub.cropper.sample.camera_java.presenter; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentActivity; + +import com.canhub.cropper.CropImage; +import com.canhub.cropper.common.CommonVersionCheck; +import com.canhub.cropper.sample.camera_java.app.SCameraFragmentJava; +import com.canhub.cropper.sample.camera_java.domain.CameraEnumDomainJava; +import com.canhub.cropper.sample.camera_java.domain.SCameraContractJava; + +import static android.app.Activity.RESULT_OK; +import static com.canhub.cropper.sample.camera_java.app.SCameraFragmentJava.CODE_PHOTO_CAMERA; + +public class SCameraPresenterJava implements SCameraContractJava.Presenter { + private SCameraContractJava.View view = null; + private boolean minVersion = CommonVersionCheck.INSTANCE.isAtLeastM23(); + private boolean request = false; + private boolean hasSystemFeature = false; + private boolean selfPermission = false; + private Context context = null; + + @Override + public void bind(SCameraContractJava.View view) { + this.view = view; + } + + @Override + public void unbind() { + view = null; + } + + @Override + public void onPermissionResult(boolean granted) { + assert view != null; + if (granted) { + view.dispatchTakePictureIntent(); + } else if (minVersion && request) { + view.showDialog(); + } else { + view.cameraPermissionLaunch(); + } + } + + @Override + public void onCreate(FragmentActivity activity, Context context) { + assert view != null; + if (activity == null || context == null) { + view.showErrorMessage("onCreate activity and/or context are null"); + return; + } + this.context = context; + + request = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA); + if (context.getPackageManager() != null) { + hasSystemFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); + } else { + hasSystemFeature = false; + } + selfPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; + } + + @Override + public void startWithUriClicked() { + assert view != null; + if (hasSystemFeature && selfPermission) { + view.dispatchTakePictureIntent(); + } else if (hasSystemFeature && minVersion && request) { + view.showDialog(); + } else if (hasSystemFeature) { + view.cameraPermissionLaunch(); + } else { + view.showErrorMessage("onCreate no case apply"); + } + } + + @Override + public void startWithoutUriClicked() { + assert view != null; + view.startCropImage(CameraEnumDomainJava.START_WITHOUT_URI); + } + + @Override + public void startPickImageActivityClicked() { + assert view != null; + view.startCropImage(CameraEnumDomainJava.START_PICK_IMG); + } + + @Override + public void startActivityForResultClicked() { + assert view != null; + view.startCropImage(CameraEnumDomainJava.START_FOR_RESULT); + } + + @Override + public void onOk() { + assert view != null; + view.cameraPermissionLaunch(); + } + + @Override + public void onCancel() { + assert view != null; + view.showErrorMessage("onCancel"); + } + + @Override + public void onActivityResult(int resultCode, int requestCode, Intent data) { + assert view != null; + if (resultCode == RESULT_OK) { + switch (requestCode) { + case CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE: { + assert (context != null); + Bitmap bitmap = CropImage.getActivityResult(data).getBitmap(context); + Log.v( + "File Path", + CropImage.getActivityResult(data).getUriFilePath(context, false) + ); + + Uri uriContent = CropImage.getActivityResult(data).getUriContent(); + if (uriContent != null) { + view.handleCropImageResult(uriContent.toString().replace("file:", "")); + } else { + view.showErrorMessage("CropImage getActivityResult return null"); + } + break; + } + case SCameraFragmentJava.CUSTOM_REQUEST_CODE: { + assert context != null; + Log.v("File Path", CropImage.getPickImageResultUriFilePath(context, data, false)); + CropImage.getPickImageResultUriFilePath(context, data, false); + Uri uri = CropImage.getPickImageResultUriContent(context, data); + if (view != null) { + view.handleCropImageResult(uri.toString().replace("file:", "")); + } + + break; + } + case CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE: { + assert context != null; + Log.v("File Path", CropImage.getPickImageResultUriFilePath(context, data, false)); + Uri uri = CropImage.getPickImageResultUriContent(context, data); + if (view != null) { + view.handleCropImageResult(uri.toString()); + } + break; + } + case CODE_PHOTO_CAMERA: { + view.startCropImage(CameraEnumDomainJava.START_WITH_URI); + break; + } + default: { + view.showErrorMessage("requestCode = " + requestCode); + break; + } + } + } else { + view.showErrorMessage("resultCode = " + resultCode); + } + } +} \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 0a58dc4a..fa84ca47 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -58,7 +58,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:layout_marginBottom="@dimen/keyline_x4" android:text="@string/sample_calling_cropimage"/> + +