diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 53449dae..a8123ea6 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,6 +1,7 @@ + diff --git a/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperDelegate.java b/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperDelegate.java index 39083303..dca39e49 100644 --- a/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperDelegate.java +++ b/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperDelegate.java @@ -5,6 +5,8 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; +import android.content.pm.PackageManager; +import androidx.core.app.ActivityCompat; import com.yalantis.ucrop.UCrop; import com.yalantis.ucrop.model.AspectRatio; @@ -20,7 +22,18 @@ import static android.app.Activity.RESULT_OK; -public class ImageCropperDelegate implements PluginRegistry.ActivityResultListener { +public class ImageCropperDelegate implements PluginRegistry.ActivityResultListener, + PluginRegistry.RequestPermissionsResultListener { + + static final int REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY = 2342; + static final int REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA = 2343; + static final int REQUEST_EXTERNAL_IMAGE_STORAGE_PERMISSION = 2344; + static final int REQUEST_CAMERA_IMAGE_PERMISSION = 2345; + static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY = 2352; + static final int REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA = 2353; + static final int REQUEST_EXTERNAL_VIDEO_STORAGE_PERMISSION = 2354; + static final int REQUEST_CAMERA_VIDEO_PERMISSION = 2355; + private final Activity activity; private MethodChannel.Result pendingResult; private FileUtils fileUtils; @@ -127,6 +140,7 @@ private UCrop.Options setupUiCustomizedOptions(UCrop.Options options, MethodCall Integer toolbarWidgetColor = call.argument("android.toolbar_widget_color"); Integer backgroundColor = call.argument("android.background_color"); Integer activeControlsWidgetColor = call.argument("android.active_controls_widget_color"); + Integer activeWidgetColor = call.argument("android.active_widget_color"); Integer dimmedLayerColor = call.argument("android.dimmed_layer_color"); Integer cropFrameColor = call.argument("android.crop_frame_color"); Integer cropGridColor = call.argument("android.crop_grid_color"); @@ -158,6 +172,9 @@ private UCrop.Options setupUiCustomizedOptions(UCrop.Options options, MethodCall if (activeControlsWidgetColor != null) { options.setActiveControlsWidgetColor(activeControlsWidgetColor); } + if (activeWidgetColor != null) { + //options.setActiveWidgetColor(activeWidgetColor); + } if (dimmedLayerColor != null) { options.setDimmedLayerColor(dimmedLayerColor); } @@ -227,4 +244,44 @@ private AspectRatio parseAspectRatioName(String name) { CropImageView.SOURCE_IMAGE_ASPECT_RATIO, 1.0f); } } -} + + @Override + public boolean onRequestPermissionsResult( + int requestCode, String[] permissions, int[] grantResults) { + boolean permissionGranted = + grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; + + switch (requestCode) { + case REQUEST_EXTERNAL_IMAGE_STORAGE_PERMISSION: + if (permissionGranted) { + } + break; + case REQUEST_EXTERNAL_VIDEO_STORAGE_PERMISSION: + if (permissionGranted) { + } + break; + case REQUEST_CAMERA_IMAGE_PERMISSION: + if (permissionGranted) { + } + break; + case REQUEST_CAMERA_VIDEO_PERMISSION: + if (permissionGranted) { + } + break; + default: + return false; + } + + if (!permissionGranted) { + switch (requestCode) { + case REQUEST_EXTERNAL_IMAGE_STORAGE_PERMISSION: + case REQUEST_EXTERNAL_VIDEO_STORAGE_PERMISSION: + case REQUEST_CAMERA_IMAGE_PERMISSION: + case REQUEST_CAMERA_VIDEO_PERMISSION: + break; + } + } + + return true; + } +} \ No newline at end of file diff --git a/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java b/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java index ca30d2e3..421597be 100644 --- a/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java +++ b/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java @@ -1,10 +1,23 @@ package vn.hunghd.flutter.plugins.imagecropper; +import android.util.Log; import android.app.Activity; - +import android.app.Application; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -17,29 +30,106 @@ /** ImageCropperPlugin */ public class ImageCropperPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { + + private class LifeCycleObserver + implements Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver { + private final Activity thisActivity; + + LifeCycleObserver(Activity activity) { + this.thisActivity = activity; + } + + @Override + public void onCreate(@NonNull LifecycleOwner owner) {} + + @Override + public void onStart(@NonNull LifecycleOwner owner) {} + + @Override + public void onResume(@NonNull LifecycleOwner owner) {} + + @Override + public void onPause(@NonNull LifecycleOwner owner) {} + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + onActivityStopped(thisActivity); + } + + @Override + public void onDestroy(@NonNull LifecycleOwner owner) { + onActivityDestroyed(thisActivity); + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) {} + + @Override + public void onActivityStarted(Activity activity) {} + + @Override + public void onActivityResumed(Activity activity) {} + + @Override + public void onActivityPaused(Activity activity) {} + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} + + @Override + public void onActivityDestroyed(Activity activity) { + if (thisActivity == activity && activity.getApplicationContext() != null) { + ((Application) activity.getApplicationContext()) + .unregisterActivityLifecycleCallbacks( + this); // Use getApplicationContext() to avoid casting failures + } + } + + @Override + public void onActivityStopped(Activity activity) { + if (thisActivity == activity) { + //delegate.saveStateBeforeResult(); + } + } + } + static { AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); } - private static final String CHANNEL = "plugins.hunghd.vn/image_cropper"; - private static ImageCropperPlugin instance; - private static MethodChannel channel; - private static ImageCropperDelegate delegate; + private static final String CHANNEL = "plugins.hunghd.vn/image_cropper"; +// private static ImageCropperPlugin instance; +// private static MethodChannel channel; +// private static ImageCropperDelegate delegate; +// private ActivityPluginBinding activityBinding; +// private Activity activity; + + private MethodChannel channel; + private ImageCropperDelegate delegate; + private FlutterPluginBinding pluginBinding; + private ActivityPluginBinding activityBinding; + private Application application; private Activity activity; + // This is null when not using v2 embedding; + private Lifecycle lifecycle; + private LifeCycleObserver observer; + private final Object initializationLock = new Object(); /** Plugin registration. */ public static void registerWith(Registrar registrar) { - if (instance == null) { - instance = new ImageCropperPlugin(); + if (registrar.activity() == null) { + return; } - if (registrar.activity() != null) { - instance.onAttachedToEngine(registrar.messenger()); - instance.onAttachedToActivity(registrar.activity()); - registrar.addActivityResultListener(instance.getActivityResultListener()); + Activity activity = registrar.activity(); + Application application = null; + if (registrar.context() != null) { + application = (Application) (registrar.context().getApplicationContext()); } + ImageCropperPlugin plugin = new ImageCropperPlugin(); + plugin.setup(registrar.messenger(), application, activity, registrar, null); } @Override @@ -53,65 +143,90 @@ public void onMethodCall(MethodCall call, Result result) { } } - private void onAttachedToEngine(BinaryMessenger messenger) { - synchronized (initializationLock) { - if (channel != null) { - return; - } + public ImageCropperPlugin() {} - channel = new MethodChannel(messenger, CHANNEL); - channel.setMethodCallHandler(this); - } - } - - private void onAttachedToActivity(Activity activity) { + @VisibleForTesting + ImageCropperPlugin(final ImageCropperDelegate delegate, final Activity activity) { + this.delegate = delegate; this.activity = activity; - delegate = new ImageCropperDelegate(activity); - } - - private PluginRegistry.ActivityResultListener getActivityResultListener() { - return delegate; } @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - onAttachedToEngine(binding.getBinaryMessenger()); + pluginBinding = binding; } @Override public void onDetachedFromEngine(FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - channel = null; + pluginBinding = null; } - @Override public void onAttachedToActivity(ActivityPluginBinding binding) { - if (getActivityResultListener() != null) { - binding.removeActivityResultListener(getActivityResultListener()); - } - onAttachedToActivity(binding.getActivity()); - binding.addActivityResultListener(getActivityResultListener()); + activityBinding = binding; + setup( + pluginBinding.getBinaryMessenger(), + (Application) pluginBinding.getApplicationContext(), + activityBinding.getActivity(), + null, + activityBinding); + } + + @Override + public void onDetachedFromActivity() { + tearDown(); } @Override public void onDetachedFromActivityForConfigChanges() { - activity = null; - delegate = null; + onDetachedFromActivity(); } @Override public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { - if (getActivityResultListener() != null) { - binding.removeActivityResultListener(getActivityResultListener()); + onAttachedToActivity(binding); + } + + private void setup( + final BinaryMessenger messenger, + final Application application, + final Activity activity, + final PluginRegistry.Registrar registrar, + final ActivityPluginBinding activityBinding) { + this.activity = activity; + this.application = application; + this.delegate = constructDelegate(activity); + channel = new MethodChannel(messenger, CHANNEL); + channel.setMethodCallHandler(this); + observer = new LifeCycleObserver(activity); + if (registrar != null) { + // V1 embedding setup for activity listeners. + application.registerActivityLifecycleCallbacks(observer); + registrar.addActivityResultListener(delegate); + registrar.addRequestPermissionsResultListener(delegate); + } else { + // V2 embedding setup for activity listeners. + activityBinding.addActivityResultListener(delegate); + activityBinding.addRequestPermissionsResultListener(delegate); + lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding); + lifecycle.addObserver(observer); } - onAttachedToActivity(binding.getActivity()); - binding.addActivityResultListener(getActivityResultListener()); } - @Override - public void onDetachedFromActivity() { - activity = null; + private void tearDown() { + activityBinding.removeActivityResultListener(delegate); + activityBinding.removeRequestPermissionsResultListener(delegate); + activityBinding = null; + lifecycle.removeObserver(observer); + lifecycle = null; delegate = null; + channel.setMethodCallHandler(null); + channel = null; + application.unregisterActivityLifecycleCallbacks(observer); + application = null; + } + + private final ImageCropperDelegate constructDelegate(final Activity setupActivity) { + return new ImageCropperDelegate(setupActivity); } -} +} \ No newline at end of file diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies new file mode 100644 index 00000000..447ce627 --- /dev/null +++ b/example/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"image_picker","path":"/Users/lolq/flutter/.pub-cache/hosted/pub.dartlang.org/image_picker-0.6.6+1/","dependencies":[]},{"name":"image_cropper","path":"/Users/lolq/workspace/flutter_image_cropper/","dependencies":[]}],"android":[{"name":"flutter_plugin_android_lifecycle","path":"/Users/lolq/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-1.0.7/","dependencies":[]},{"name":"image_picker","path":"/Users/lolq/flutter/.pub-cache/hosted/pub.dartlang.org/image_picker-0.6.6+1/","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"image_cropper","path":"/Users/lolq/workspace/flutter_image_cropper/","dependencies":["flutter_plugin_android_lifecycle"]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"image_cropper","dependencies":["flutter_plugin_android_lifecycle"]}],"date_created":"2020-05-12 18:42:18.873526","version":"1.18.0-11.0.pre.6"} \ No newline at end of file diff --git a/example/android/android.iml b/example/android/android.iml new file mode 100644 index 00000000..69525d6b --- /dev/null +++ b/example/android/android.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/android/app/app.iml b/example/android/app/app.iml index 641aff9a..160bb620 100644 --- a/example/android/app/app.iml +++ b/example/android/app/app.iml @@ -121,11 +121,11 @@ - - - - - + + + + + @@ -145,6 +145,7 @@ + \ No newline at end of file diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 00000000..bd346be0 --- /dev/null +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/lolq/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/lolq/workspace/flutter_image_cropper/example" +export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_BUILD_DIR=build" +export "SYMROOT=${SOURCE_ROOT}/../build/ios" +export "OTHER_LDFLAGS=$(inherited) -framework Flutter" +export "FLUTTER_FRAMEWORK_DIR=/Users/lolq/flutter/bin/cache/artifacts/engine/ios" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=false" +export "TREE_SHAKE_ICONS=false" diff --git a/pubspec.yaml b/pubspec.yaml index 0b5277d0..cd86e724 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ environment: dependencies: flutter: sdk: flutter + flutter_plugin_android_lifecycle: ^1.0.7 flutter: plugin: