diff --git a/README.md b/README.md index 7468987..0f10ae9 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ # ImagePicker +[![Release](https://jitpack.io/v/TranscodeGroup/ImagePicker.svg)](https://jitpack.io/#TranscodeGroup/ImagePicker) + > 项目地址:https://github.com/linchaolong/ImagePicker ImagePicker 是 Android 下的图片选择与裁剪开源库,基于 [Android-Image-Cropper](https://github.com/ArthurHub/Android-Image-Cropper) 并做了如下改进: diff --git a/app/build.gradle b/app/build.gradle index b8141cb..c508fa1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" + namespace 'com.linchaolong.android.imagepicker.demo' + compileSdk 34 defaultConfig { applicationId "com.linchaolong.android.imagepicker.demo" - minSdkVersion 14 - targetSdkVersion 28 + minSdkVersion 21 + targetSdkVersion 34 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -20,13 +20,13 @@ android { } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - implementation 'com.android.support:appcompat-v7:28.0.0' - testImplementation 'junit:junit:4.12' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.facebook.fresco:fresco:1.1.0' - implementation 'com.linchaolong.android:imagepicker:1.5' -// compile project(path: ':library') +// implementation 'com.linchaolong.android:imagepicker:1.5' + implementation project(path: ':library') + + testImplementation "junit:junit:4.13.2" + androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") } diff --git a/app/src/androidTest/java/com/linchaolong/android/imagepicker/demo/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/linchaolong/android/imagepicker/demo/ExampleInstrumentedTest.java index f916775..e658b42 100644 --- a/app/src/androidTest/java/com/linchaolong/android/imagepicker/demo/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/com/linchaolong/android/imagepicker/demo/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package com.linchaolong.android.imagepicker.demo; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,7 +17,7 @@ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.linchaolong.android.imagepicker", appContext.getPackageName()); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8c3f576..5275b23 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,12 @@ + xmlns:tools="http://schemas.android.com/tools"> + + - + android:theme="@style/AppTheme" + tools:targetApi="tiramisu"> + diff --git a/app/src/main/java/com/linchaolong/android/imagepicker/demo/FragmentTestActivity.java b/app/src/main/java/com/linchaolong/android/imagepicker/demo/FragmentTestActivity.java index 4a2975d..cb10c07 100644 --- a/app/src/main/java/com/linchaolong/android/imagepicker/demo/FragmentTestActivity.java +++ b/app/src/main/java/com/linchaolong/android/imagepicker/demo/FragmentTestActivity.java @@ -1,6 +1,6 @@ package com.linchaolong.android.imagepicker.demo; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class FragmentTestActivity extends AppCompatActivity { diff --git a/app/src/main/java/com/linchaolong/android/imagepicker/demo/MainActivity.java b/app/src/main/java/com/linchaolong/android/imagepicker/demo/MainActivity.java index d91cf39..a1d028a 100644 --- a/app/src/main/java/com/linchaolong/android/imagepicker/demo/MainActivity.java +++ b/app/src/main/java/com/linchaolong/android/imagepicker/demo/MainActivity.java @@ -3,9 +3,10 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; import android.view.View; + +import androidx.appcompat.app.AppCompatActivity; + import com.facebook.drawee.generic.RoundingParams; import com.facebook.drawee.view.SimpleDraweeView; import com.linchaolong.android.imagepicker.ImagePicker; @@ -14,76 +15,71 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListener { - private ImagePicker imagePicker = new ImagePicker(); - private SimpleDraweeView draweeView; - - @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + private ImagePicker imagePicker; + private SimpleDraweeView draweeView; - // 设置标题 - imagePicker.setTitle("设置头像"); - // 设置是否裁剪图片 - imagePicker.setCropImage(true); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + findViewById(R.id.fragmentTest).setOnClickListener(this); + draweeView = findViewById(R.id.draweeView); + draweeView.setOnClickListener(this); - findViewById(R.id.fragmentTest).setOnClickListener(this); - draweeView = findViewById(R.id.draweeView); - draweeView.setOnClickListener(this); - } + imagePicker = new ImagePicker(this, new ImagePicker.Callback() { + // 选择图片回调 + @Override + public void onPickImage(Uri imageUri) { - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - imagePicker.onActivityResult(this, requestCode, resultCode, data); - } + } - @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - imagePicker.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } + // 裁剪图片回调 + @Override + public void onCropImage(Uri imageUri) { + draweeView.setImageURI(imageUri); + draweeView.getHierarchy().setRoundingParams(RoundingParams.asCircle()); + } - private void startChooser() { - // 启动图片选择器 - imagePicker.startChooser(this, new ImagePicker.Callback() { - // 选择图片回调 - @Override public void onPickImage(Uri imageUri) { + // 自定义裁剪配置 + @Override + public void cropConfig(CropImage.ActivityBuilder builder) { + builder + // 是否启动多点触摸 + .setMultiTouchEnabled(false) + // 设置网格显示模式 + .setGuidelines(CropImageView.Guidelines.OFF) + // 圆形/矩形 + .setCropShape(CropImageView.CropShape.RECTANGLE) + // 调整裁剪后的图片最终大小 + .setRequestedSize(960, 540) + // 宽高比 + .setAspectRatio(16, 9); + } - } + // 用户拒绝授权回调 + @Override + public void onPermissionDenied(String permissions) { + } + }); - // 裁剪图片回调 - @Override public void onCropImage(Uri imageUri) { - draweeView.setImageURI(imageUri); - draweeView.getHierarchy().setRoundingParams(RoundingParams.asCircle()); - } - - // 自定义裁剪配置 - @Override public void cropConfig(CropImage.ActivityBuilder builder) { - builder - // 是否启动多点触摸 - .setMultiTouchEnabled(false) - // 设置网格显示模式 - .setGuidelines(CropImageView.Guidelines.OFF) - // 圆形/矩形 - .setCropShape(CropImageView.CropShape.RECTANGLE) - // 调整裁剪后的图片最终大小 - .setRequestedSize(960, 540) - // 宽高比 - .setAspectRatio(16, 9); - } + // 设置标题 + imagePicker.setTitle("设置头像"); + // 设置是否裁剪图片 + imagePicker.setCropImage(true); + } - // 用户拒绝授权回调 - @Override public void onPermissionDenied(int requestCode, String[] permissions, - int[] grantResults) { - } - }); - } + private void startChooser() { + // 启动图片选择器 + imagePicker.startChooser(); + } - @Override public void onClick(View v) { - if (v.getId() == R.id.draweeView) { - startChooser(); - } else if (v.getId() == R.id.fragmentTest) { - startActivity(new Intent(this, FragmentTestActivity.class)); + @Override + public void onClick(View v) { + if (v.getId() == R.id.draweeView) { + startChooser(); + } else if (v.getId() == R.id.fragmentTest) { + startActivity(new Intent(this, FragmentTestActivity.class)); + } } - } } diff --git a/app/src/main/java/com/linchaolong/android/imagepicker/demo/TestFragment.java b/app/src/main/java/com/linchaolong/android/imagepicker/demo/TestFragment.java index f39d67d..6c04866 100644 --- a/app/src/main/java/com/linchaolong/android/imagepicker/demo/TestFragment.java +++ b/app/src/main/java/com/linchaolong/android/imagepicker/demo/TestFragment.java @@ -1,22 +1,19 @@ package com.linchaolong.android.imagepicker.demo; import android.content.DialogInterface; -import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v7.app.AlertDialog; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import com.facebook.drawee.generic.RoundingParams; -import com.facebook.drawee.view.SimpleDraweeView; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; + import com.linchaolong.android.imagepicker.ImagePicker; -import com.linchaolong.android.imagepicker.cropper.CropImage; import com.linchaolong.android.imagepicker.cropper.CropImageView; /** @@ -24,64 +21,60 @@ */ public class TestFragment extends Fragment { - private ImagePicker imagePicker = new ImagePicker(); - private ImageView imageView; - private CropImageView cropImageView; - - @Nullable @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View contentView = inflater.inflate(R.layout.fragment_test, null); - init(contentView); - return contentView; - } - - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - imagePicker.onActivityResult(this, requestCode, resultCode, data); - } - - @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - imagePicker.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - private void init(View contentView) { - imageView = (ImageView) contentView.findViewById(R.id.imageView); - cropImageView = (CropImageView) contentView.findViewById(R.id.cropImageView); - contentView.setOnClickListener(new View.OnClickListener() { - @Override public void onClick(View v) { - startCameraOrGallery(); - } - }); - } + private ImagePicker imagePicker; + private ImageView imageView; + private CropImageView cropImageView; - private void startCameraOrGallery() { - new AlertDialog.Builder(getActivity()).setTitle("设置头像") - .setItems(new String[] { "从相册中选取图片", "拍照" }, new DialogInterface.OnClickListener() { - @Override public void onClick(DialogInterface dialog, int which) { - // 回调 - ImagePicker.Callback callback = new ImagePicker.Callback() { - @Override public void onPickImage(Uri imageUri) { - } - - @Override public void onCropImage(Uri imageUri) { + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + imagePicker = new ImagePicker(this, new ImagePicker.Callback() { + @Override + public void onCropImage(Uri imageUri) { imageView.setImageURI(imageUri); cropImageView.setImageUriAsync(imageUri); - } - }; - if (which == 0) { - // 从相册中选取图片 - imagePicker.startGallery(TestFragment.this, callback); - } else { - // 拍照 - imagePicker.startCamera(TestFragment.this, callback); } - } - }) - .show() - .getWindow() - .setGravity(Gravity.BOTTOM); - } + }); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View contentView = inflater.inflate(R.layout.fragment_test, null); + init(contentView); + return contentView; + } + + private void init(View contentView) { + imageView = (ImageView) contentView.findViewById(R.id.imageView); + cropImageView = (CropImageView) contentView.findViewById(R.id.cropImageView); + contentView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startCameraOrGallery(); + } + }); + } + + private void startCameraOrGallery() { + new AlertDialog.Builder(getActivity()).setTitle("设置头像") + .setItems(new String[]{"选择器(Chooser)", "从相册中选取图片(Gallery)", "拍照(Camera)"}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + imagePicker.startChooser(); + } else if (which == 1) { + // 从相册中选取图片 + imagePicker.startGallery(); + } else { + // 拍照 + imagePicker.startCamera(); + } + } + }) + .show() + .getWindow() + .setGravity(Gravity.BOTTOM); + } } diff --git a/build.gradle b/build.gradle index 51cdacf..8649ba1 100644 --- a/build.gradle +++ b/build.gradle @@ -3,20 +3,19 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } + ext.kotlin_version = '1.8.22' dependencies { - classpath 'com.android.tools.build:gradle:3.3.0-alpha13' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - classpath 'com.novoda:bintray-release:0.5.0' + classpath 'com.android.tools.build:gradle:8.1.4' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { google() - jcenter() + mavenCentral() } // 中文注释 tasks.withType(Javadoc) { diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 90ee2d1..8d8fc5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Oct 16 14:47:26 CST 2018 +#Thu Jul 18 14:15:25 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..efde7bf --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +jdk: + - openjdk17 diff --git a/library/build.gradle b/library/build.gradle index b8002cc..6ec5d57 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,61 +1,58 @@ apply plugin: 'com.android.library' -apply plugin: 'com.novoda.bintray-release' +apply plugin: 'maven-publish' +apply plugin: 'kotlin-android' android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" + namespace 'com.linchaolong.android.imagepicker' + compileSdk 34 defaultConfig { - minSdkVersion 10 - targetSdkVersion 28 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + minSdkVersion 21 + targetSdkVersion 34 + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } -} -publish { - // 读取 local.properties 中配置 - Properties properties = new Properties() - File localPropertiesFile = rootProject.file("local.properties"); - if (localPropertiesFile.exists()) { - properties.load(localPropertiesFile.newDataInputStream()) - } - // 用户名 - bintrayUser = properties.getProperty("bintrayUser") - if(bintrayUser == null && project.hasProperty("bintrayUser")){ - bintrayUser = project.property("bintrayUser") + publishing { + singleVariant('release') { + withSourcesJar() + withJavadocJar() + } } - // apkKey - bintrayKey = properties.getProperty("bintrayKey") - if(bintrayKey == null && project.hasProperty("bintrayKey")){ - bintrayKey = project.property("bintrayKey") +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + groupId = 'com.linchaolong.android' + artifactId = 'imagepicker' + version = '1.5.1' + from components.release + pom { + name = 'ImagePicker' + // 项目描述 + description = 'android image picker and cropper library' + // 项目地址 + url = 'https://github.com/linchaolong/ImagePicker' + } + } + } } - // 仓库名称,默认maven - repoName = 'maven' - // 组织名称 - userOrg = 'linchaolong' - //compile 'groupId:artifactId:publishVersion' - groupId = 'com.linchaolong.android' - artifactId = 'imagepicker' - publishVersion = '1.5' - // 项目描述 - desc = 'android image picker and cropper library' - // 项目地址 - website = 'https://github.com/linchaolong/ImagePicker' - // 如果设置为true,它将运行所有内容,但不会将包上传到bintray。如果是false,那么它会正常上传。 - dryRun = false } + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - implementation 'com.android.support:appcompat-v7:28.0.0' - testImplementation 'junit:junit:4.12' + implementation 'androidx.appcompat:appcompat:1.7.0' + + testImplementation "junit:junit:4.13.2" + androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") } diff --git a/library/src/androidTest/java/com/linchaolong/android/imagepicker/ExampleInstrumentedTest.java b/library/src/androidTest/java/com/linchaolong/android/imagepicker/ExampleInstrumentedTest.java index dfc3c18..7198f5a 100644 --- a/library/src/androidTest/java/com/linchaolong/android/imagepicker/ExampleInstrumentedTest.java +++ b/library/src/androidTest/java/com/linchaolong/android/imagepicker/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package com.linchaolong.android.imagepicker; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,7 +17,7 @@ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.linchaolong.android.imagepicker.test", appContext.getPackageName()); } diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index 499b6f9..aa296c6 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,14 +1,16 @@ + xmlns:tools="http://schemas.android.com/tools"> - - + + @@ -17,6 +19,16 @@ android:resource="@xml/provider_paths"/> + + + + + + + diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/ImagePicker.java b/library/src/main/java/com/linchaolong/android/imagepicker/ImagePicker.java index 4f6d4ba..23faaaf 100644 --- a/library/src/main/java/com/linchaolong/android/imagepicker/ImagePicker.java +++ b/library/src/main/java/com/linchaolong/android/imagepicker/ImagePicker.java @@ -1,5 +1,9 @@ package com.linchaolong.android.imagepicker; +import static androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia; +import static androidx.activity.result.contract.ActivityResultContracts.RequestPermission; +import static androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; + import android.Manifest; import android.app.Activity; import android.content.Context; @@ -9,418 +13,324 @@ import android.net.Uri; import android.os.Parcelable; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.content.FileProvider; import android.text.TextUtils; import android.util.Log; + +import androidx.activity.ComponentActivity; +import androidx.activity.result.ActivityResultCaller; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.PickVisualMediaRequest; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + import com.linchaolong.android.imagepicker.cropper.CropImage; import com.linchaolong.android.imagepicker.cropper.CropImageView; + import java.io.File; import java.io.FileNotFoundException; import java.util.List; /** * 图片选择,裁剪封装类 - * + *

* Created by linchaolong on 2017/3/9. */ public class ImagePicker { - private static final String TAG = "ImagePicker"; - - private Callback callback; - private boolean isCropImage = true; - private CharSequence title; - - private Uri pickImageUri; - private Uri cropImageUri; - - /** - * 设置是否裁剪图片 - * - * @param cropImage 是否裁剪图片 - */ - public void setCropImage(boolean cropImage) { - isCropImage = cropImage; - } - - /** - * 设置标题 - * - * @param title - */ - public void setTitle(CharSequence title) { - this.title = title; - } - - /** - * 启动图片选择器 - * - * @param activity {@link Activity} - * @param callback {@link Callback} - */ - public void startChooser(Activity activity, @NonNull Callback callback) { - this.callback = callback; - if (CropImage.isExplicitCameraPermissionRequired(activity)) { - ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.CAMERA }, - CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); - } else { - activity.startActivityForResult( - CropImage.getPickImageChooserIntent(activity, getTitle(activity), false), - CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - } - - /** - * 启动图片选择器 - * - * @param fragment {@link Fragment} - * @param callback {@link Callback} - */ - public void startChooser(Fragment fragment, @NonNull Callback callback) { - this.callback = callback; - if (CropImage.isExplicitCameraPermissionRequired(fragment.getActivity())) { - fragment.requestPermissions(new String[] { Manifest.permission.CAMERA }, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); - } else { - fragment.startActivityForResult( - CropImage.getPickImageChooserIntent(fragment.getActivity(), getTitle(fragment.getActivity()), false), - CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - } - - /** - * 启动照相机 - * - * @param activity - * @param callback - */ - public void startCamera(Activity activity, @NonNull Callback callback) { - this.callback = callback; - if (CropImage.isExplicitCameraPermissionRequired(activity)) { - ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.CAMERA }, - CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); - } else { - activity.startActivityForResult(CropImage.getCameraIntent(activity, null), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - } - - /** - * 启动照相机 - * - * @param fragment - * @param callback - */ - public void startCamera(Fragment fragment, @NonNull Callback callback) { - this.callback = callback; - if (CropImage.isExplicitCameraPermissionRequired(fragment.getActivity())) { - fragment.requestPermissions(new String[] { Manifest.permission.CAMERA }, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); - } else { - fragment.startActivityForResult(CropImage.getCameraIntent(fragment.getActivity(), null), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - } - - /** - * 启动图库选择器 - * - * @param activity - * @param callback - */ - public void startGallery(Activity activity, @NonNull Callback callback) { - this.callback = callback; - activity.startActivityForResult(getGalleryIntent(activity, false), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - - /** - * 启动图库选择器 - * - * @param fragment - * @param callback - */ - public void startGallery(Fragment fragment, @NonNull Callback callback) { - this.callback = callback; - fragment.startActivityForResult(getGalleryIntent(fragment.getActivity(), false), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - - protected CharSequence getTitle(Context context){ - if(TextUtils.isEmpty(title)){ - return context.getString(R.string.pick_image_intent_chooser_title); + private static final String TAG = "ImagePicker"; + + @NonNull + private final ActivityResultCaller caller; + @NonNull + private final Callback callback; + private boolean isCropImage = true; + private CharSequence title; + + private Uri imageUrlForRequestPermission; + private final ActivityResultLauncher galleryLauncher; + private final ActivityResultLauncher chooserLauncher; + private final ActivityResultLauncher cropLauncher; + private final ActivityResultLauncher requestCameraPermissionForChooser; + private final ActivityResultLauncher requestCameraPermissionForCamera; + private ActivityResultLauncher requestReadPermission; + + private Context getContext() { + if (caller instanceof ComponentActivity) { + return (Context) caller; + } else if (caller instanceof Fragment) { + return ((Fragment) caller).requireContext(); + } + throw new IllegalArgumentException("caller must be ComponentActivity or Fragment: " + caller); } - return title; - } - protected Intent getGalleryIntent(Context context, boolean includeDocuments){ - PackageManager packageManager = context.getPackageManager(); + public ImagePicker(ActivityResultCaller caller, @NonNull Callback callback) { + this.caller = caller; + this.callback = callback; + galleryLauncher = caller.registerForActivityResult( + new WithInputActivityResultContract<>( + new PickVisualMedia(), + new PickVisualMediaRequest.Builder() + .setMediaType(PickVisualMedia.ImageOnly.INSTANCE) + .build() + ), + imageUri -> { + if (imageUri == null) { + return; + } + handlePickImage(imageUri); + } + ); + chooserLauncher = caller.registerForActivityResult(new StartActivityForResult(), result -> { + if (result.getResultCode() != Activity.RESULT_OK) { + return; + } + Uri imageUri = CropImage.getPickImageResultUri(getContext(), result.getData()); + // 检查读取文件权限 + if (CropImage.isReadExternalStoragePermissionsRequired(getContext(), imageUri)) { + this.imageUrlForRequestPermission = imageUri; + requestReadPermission.launch(null); + } else { + // 选择图片回调 + handlePickImage(imageUri); + } + }); + cropLauncher = caller.registerForActivityResult(new StartActivityForResult(), result -> { + if (result.getResultCode() != Activity.RESULT_OK) { + return; + } + + Intent data = result.getData(); + if (data != null) { + handleCropResult(getContext(), CropImage.getActivityResult(data)); + } + }); + requestReadPermission = caller.registerForActivityResult( + new WithInputActivityResultContract<>(new RequestPermission(), Manifest.permission.READ_EXTERNAL_STORAGE), + result -> { + if (!result) { + callback.onPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE); + return; + } + if (imageUrlForRequestPermission != null) { + handlePickImage(imageUrlForRequestPermission); + imageUrlForRequestPermission = null; + } + } + ); + requestCameraPermissionForChooser = caller.registerForActivityResult( + new WithInputActivityResultContract<>(new RequestPermission(), Manifest.permission.CAMERA), + result -> { + if (!result) { + callback.onPermissionDenied(Manifest.permission.CAMERA); + return; + } + chooserLauncher.launch(CropImage.getPickImageChooserIntent(getContext(), getTitle(getContext()), false)); + } + ); + requestCameraPermissionForCamera = caller.registerForActivityResult( + new WithInputActivityResultContract<>(new RequestPermission(), Manifest.permission.CAMERA), + result -> { + if (!result) { + callback.onPermissionDenied(Manifest.permission.CAMERA); + return; + } + chooserLauncher.launch(CropImage.getCameraIntent(getContext(), null)); + } + ); + } - List galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments); - if (galleryIntents.size() == 0) { - // if no intents found for get-content try pick intent action (Huawei P9). - galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_PICK, includeDocuments); + /** + * 设置是否裁剪图片 + * + * @param cropImage 是否裁剪图片 + */ + public void setCropImage(boolean cropImage) { + isCropImage = cropImage; } - Intent target; - if (galleryIntents.isEmpty()) { - target = new Intent(); - } else { - target = galleryIntents.get(galleryIntents.size() - 1); - galleryIntents.remove(galleryIntents.size() - 1); + /** + * 设置标题 + * + * @param title + */ + public void setTitle(CharSequence title) { + this.title = title; } - // Create a chooser from the main intent - Intent chooserIntent = Intent.createChooser(target, getTitle(context)); - - // Add all other intents - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, galleryIntents.toArray(new Parcelable[galleryIntents.size()])); - - return chooserIntent; - } - - /** - * 图片选择/裁剪结果回调,在 {@link Activity#onActivityResult(int, int, Intent)} 中调用 - * - * @param activity - * @param requestCode - * @param resultCode - * @param data - */ - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - onActivityResultInner(activity, null, requestCode, resultCode, data); - } - - /** - * 图片选择/裁剪结果回调,在 {@link Fragment#onActivityResult(int, int, Intent)} 中调用 - * - * @param fragment - * @param requestCode - * @param resultCode - * @param data - */ - public void onActivityResult(Fragment fragment, int requestCode, int resultCode, Intent data) { - onActivityResultInner(null, fragment, requestCode, resultCode, data); - } - - private void onActivityResultInner(Activity activity, Fragment fragment, int requestCode, int resultCode, Intent data) { - if (resultCode == Activity.RESULT_OK) { - Context context; - if (activity != null) { - context = activity; - }else{ - context = fragment.getActivity(); - } - if (requestCode == CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE) { - pickImageUri = CropImage.getPickImageResultUri(context, data); - // 检查读取文件权限 - if (CropImage.isReadExternalStoragePermissionsRequired(context, pickImageUri)) { - if (activity != null) { - ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, - CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); - }else{ - fragment.requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, - CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); - } + /** + * 启动图片选择器 + */ + public void startChooser() { + if (CropImage.isExplicitCameraPermissionRequired(getContext())) { + requestCameraPermissionForChooser.launch(null); } else { - // 选择图片回调 - if (activity != null) { - handlePickImage(activity, pickImageUri); - }else{ - handlePickImage(fragment, pickImageUri); - } + chooserLauncher.launch(CropImage.getPickImageChooserIntent(getContext(), getTitle(getContext()), false)); } - } else if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { - // 裁剪图片回调 - handleCropResult(context, CropImage.getActivityResult(data)); - } } - } - - /** - * 获取文件的真实路径,比如:content://media/external/images/media/74275 的真实路径 file:///storage/sdcard0/Pictures/X.jpg - * - * http://stackoverflow.com/questions/20028319/how-to-convert-content-media-external-images-media-y-to-file-storage-sdc - * - * @param context - * @param contentUri - * @return - */ - private static String getRealPathFromUri(Context context, Uri contentUri) { - Cursor cursor = null; - try { - String[] proj = { MediaStore.Images.Media.DATA }; - cursor = context.getContentResolver().query(contentUri, proj, null, null, null); - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - cursor.moveToFirst(); - return cursor.getString(column_index); - } finally { - if (cursor != null) { - cursor.close(); - } + + /** + * 启动照相机 + */ + public void startCamera() { + if (CropImage.isExplicitCameraPermissionRequired(getContext())) { + requestCameraPermissionForCamera.launch(null); + } else { + chooserLauncher.launch(CropImage.getCameraIntent(getContext(), null)); + } } - } - - /** - * 授权结果回调,在 {@link Activity#onRequestPermissionsResult(int, String[], int[])} 中调用 - * - * @param activity - * @param requestCode - * @param permissions - * @param grantResults - */ - public void onRequestPermissionsResult(Activity activity, int requestCode, String permissions[], - int[] grantResults) { - onRequestPermissionsResultInner(activity, null, requestCode, permissions, grantResults); - } - - /** - * 授权结果回调,在 {@link Fragment#onRequestPermissionsResult(int, String[], int[])} 中调用 - * - * @param fragment - * @param requestCode - * @param permissions - * @param grantResults - */ - public void onRequestPermissionsResult(Fragment fragment, int requestCode, String permissions[], - int[] grantResults) { - onRequestPermissionsResultInner(null, fragment, requestCode, permissions, grantResults); - } - - private void onRequestPermissionsResultInner(Activity activity, Fragment fragment, int requestCode, String permissions[], - int[] grantResults) { - if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (activity != null) { - CropImage.startPickImageActivity(activity); - }else{ - CropImage.startPickImageActivity(fragment); + + /** + * 启动图库选择器 + */ + public void startGallery() { + if (PickVisualMedia.isPhotoPickerAvailable(getContext())) { + galleryLauncher.launch(null); + } else { + chooserLauncher.launch(getGalleryIntent(getContext(), false)); } - } else { - // 用户拒绝授权 - if(callback != null){ - callback.onPermissionDenied(requestCode, permissions, grantResults); + } + + + protected CharSequence getTitle(Context context) { + if (TextUtils.isEmpty(title)) { + return context.getString(R.string.pick_image_intent_chooser_title); } - } + return title; } - if (requestCode == CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE) { - if (cropImageUri != null - && grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (activity != null) { - handlePickImage(activity, cropImageUri); - }else{ - handlePickImage(fragment, cropImageUri); + + protected Intent getGalleryIntent(Context context, boolean includeDocuments) { + PackageManager packageManager = context.getPackageManager(); + + List galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments); + if (galleryIntents.size() == 0) { + // if no intents found for get-content try pick intent action (Huawei P9). + galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_PICK, includeDocuments); } - } else { - // 用户拒绝授权 - if(callback != null){ - callback.onPermissionDenied(requestCode, permissions, grantResults); + + Intent target; + if (galleryIntents.isEmpty()) { + target = new Intent(); + } else { + target = galleryIntents.get(galleryIntents.size() - 1); + galleryIntents.remove(galleryIntents.size() - 1); } - } - } - } - - /** - * 裁剪图片结果回调 - */ - private void handleCropResult(Context context, CropImageView.CropResult result) { - if (result.getError() == null) { - cropImageUri = result.getUri(); - if(callback != null){ - callback.onCropImage(handleUri(context, cropImageUri)); - } - } else { - Log.e(TAG, "handleCropResult error", result.getError()); - } - } - - private void handlePickImage(Activity activity, Uri imageUri) { - handlePickImageInner(activity, null, imageUri); - } - - private void handlePickImage(Fragment fragment, Uri imageUri) { - handlePickImageInner(null, fragment, imageUri); - } - - /** - * 选择图片结果回调 - */ - private void handlePickImageInner(Activity activity, Fragment fragment, Uri imageUri) { - if(callback != null){ - Context context; - if (activity != null) { - context = activity; - }else{ - context = fragment.getContext(); - } - callback.onPickImage(handleUri(context, imageUri)); - } - if(!isCropImage){ - return; - } - // 打开裁剪图片界面 - CropImage.ActivityBuilder builder = CropImage.activity(imageUri); - // 裁剪配置 - callback.cropConfig(builder); - // 启动裁剪界面 - if (activity != null) { - builder.start(activity); - }else{ - builder.start(fragment.getActivity(), fragment); - } - } - - /** - * 处理返回图片的 uri,content 协议自动转换 file 协议 - * ,避免 {@link FileNotFoundException} - * - * @param context - * @param imageUri - * @return - */ - private Uri handleUri(Context context, Uri imageUri){ - if("content".equals(imageUri.getScheme())){ - String realPathFromUri = getRealPathFromUri(context, imageUri); - if (!TextUtils.isEmpty(realPathFromUri)) { - return Uri.fromFile(new File(realPathFromUri)); - } + + // Create a chooser from the main intent + Intent chooserIntent = Intent.createChooser(target, getTitle(context)); + + // Add all other intents + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, galleryIntents.toArray(new Parcelable[galleryIntents.size()])); + + return chooserIntent; } - return imageUri; - } - public static abstract class Callback{ /** - * 图片选择回调 - * @param imageUri + * 获取文件的真实路径,比如:content://media/external/images/media/74275 的真实路径 file:///storage/sdcard0/Pictures/X.jpg + *

+ * http://stackoverflow.com/questions/20028319/how-to-convert-content-media-external-images-media-y-to-file-storage-sdc + * + * @param context + * @param contentUri + * @return */ - public abstract void onPickImage(Uri imageUri); + private static String getRealPathFromUri(Context context, Uri contentUri) { + Cursor cursor = null; + try { + String[] proj = {MediaStore.Images.Media.DATA}; + cursor = context.getContentResolver().query(contentUri, proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + cursor.moveToFirst(); + return cursor.getString(column_index); + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + /** - * 图片裁剪回调 - * - * @param imageUri + * 裁剪图片结果回调 */ - public void onCropImage(Uri imageUri){} + private void handleCropResult(Context context, CropImageView.CropResult result) { + if (result.getError() == null) { + Uri cropImageUri = result.getUri(); + callback.onCropImage(handleUri(context, cropImageUri)); + } else { + Log.e(TAG, "handleCropResult error", result.getError()); + } + } /** - * 图片裁剪配置 + * 选择图片结果回调 */ - public void cropConfig(CropImage.ActivityBuilder builder){ - // 默认配置 - builder.setMultiTouchEnabled(false) - .setCropShape(CropImageView.CropShape.OVAL) - .setRequestedSize(640, 640) - .setAspectRatio(5, 5); + private void handlePickImage(Uri imageUri) { + callback.onPickImage(handleUri(getContext(), imageUri)); + if (!isCropImage) { + return; + } + // 打开裁剪图片界面 + CropImage.ActivityBuilder builder = CropImage.activity(imageUri); + // 裁剪配置 + callback.cropConfig(builder); + // 启动裁剪界面 + builder.start(getContext(), cropLauncher); } /** - * 用户拒绝授权回调 + * 处理返回图片的 uri,content 协议自动转换 file 协议 + * ,避免 {@link FileNotFoundException} * - * @param requestCode - * @param permissions - * @param grantResults + * @param context + * @param imageUri + * @return */ - public void onPermissionDenied(int requestCode, String permissions[], int[] grantResults){} - } + private Uri handleUri(Context context, Uri imageUri) { + if ("content".equals(imageUri.getScheme())) { + String realPathFromUri = getRealPathFromUri(context, imageUri); + if (!TextUtils.isEmpty(realPathFromUri)) { + return Uri.fromFile(new File(realPathFromUri)); + } + } + return imageUri; + } + + public static abstract class Callback { + + /** + * 图片选择回调 + * + * @param imageUri + */ + public void onPickImage(Uri imageUri) { + } + + /** + * 图片裁剪回调 + * + * @param imageUri + */ + public void onCropImage(Uri imageUri) { + } + + /** + * 图片裁剪配置 + */ + public void cropConfig(CropImage.ActivityBuilder builder) { + // 默认配置 + builder.setMultiTouchEnabled(false) + .setCropShape(CropImageView.CropShape.OVAL) + .setRequestedSize(640, 640) + .setAspectRatio(5, 5); + } + + /** + * 用户拒绝授权回调 + */ + public void onPermissionDenied(String permission) { + } + } } diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/Utils.java b/library/src/main/java/com/linchaolong/android/imagepicker/Utils.java index c630589..2f7e157 100644 --- a/library/src/main/java/com/linchaolong/android/imagepicker/Utils.java +++ b/library/src/main/java/com/linchaolong/android/imagepicker/Utils.java @@ -5,7 +5,7 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; -import android.support.v4.content.FileProvider; +import androidx.core.content.FileProvider; import java.io.File; /** diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/WithInputActivityResultContract.java b/library/src/main/java/com/linchaolong/android/imagepicker/WithInputActivityResultContract.java new file mode 100644 index 0000000..99030ce --- /dev/null +++ b/library/src/main/java/com/linchaolong/android/imagepicker/WithInputActivityResultContract.java @@ -0,0 +1,36 @@ +package com.linchaolong.android.imagepicker; + +import android.content.Context; +import android.content.Intent; + +import androidx.activity.result.contract.ActivityResultContract; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class WithInputActivityResultContract extends ActivityResultContract { + private final ActivityResultContract mContract; + private final I mInput; + + public WithInputActivityResultContract(ActivityResultContract contract, I input) { + this.mContract = contract; + this.mInput = input; + } + + + @NonNull + @Override + public Intent createIntent(@NonNull Context context, Void i) { + return mContract.createIntent(context, mInput); + } + + @Override + public O parseResult(int i, @Nullable Intent intent) { + return mContract.parseResult(i, intent); + } + + @Nullable + @Override + public SynchronousResult getSynchronousResult(@NonNull Context context, Void input) { + return mContract.getSynchronousResult(context, mInput); + } +} diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImage.java b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImage.java index 1faa97f..ba016a5 100644 --- a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImage.java +++ b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImage.java @@ -13,7 +13,6 @@ package com.linchaolong.android.imagepicker.cropper; import android.Manifest; -import android.app.Activity; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -33,12 +32,14 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.content.FileProvider; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.linchaolong.android.imagepicker.R; import com.linchaolong.android.imagepicker.Utils; + import java.io.File; import java.io.InputStream; import java.util.ArrayList; @@ -130,21 +131,6 @@ public static Bitmap toOvalBitmap(@NonNull Bitmap bitmap) { return output; } - /** - * Start an activity to get image for cropping using chooser intent that will have all the available - * applications for the device like camera (MyCamera), galery (Photos), store apps (Dropbox), etc.
- * Use "pick_image_intent_chooser_title" string resource to override pick chooser title. - * - * @param activity the activity to be used to start activity from - */ - public static void startPickImageActivity(@NonNull Activity activity) { - activity.startActivityForResult(getPickImageChooserIntent(activity), PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - - public static void startPickImageActivity(@NonNull Fragment fragment){ - fragment.startActivityForResult(getPickImageChooserIntent(fragment.getActivity()), PICK_IMAGE_CHOOSER_REQUEST_CODE); - } - /** * Create a chooser intent to select the source to get image from.
* The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).
@@ -155,7 +141,7 @@ public static void startPickImageActivity(@NonNull Fragment fragment){ */ public static Intent getPickImageChooserIntent(@NonNull Context context) { return getPickImageChooserIntent(context, context.getString( - R.string.pick_image_intent_chooser_title), false); + R.string.pick_image_intent_chooser_title), false); } /** @@ -163,8 +149,8 @@ public static Intent getPickImageChooserIntent(@NonNull Context context) { * The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).
* All possible sources are added to the intent chooser. * - * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. - * @param title the title to use for the chooser UI + * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. + * @param title the title to use for the chooser UI * @param includeDocuments if to include KitKat documents activity containing all sources */ public static Intent getPickImageChooserIntent(@NonNull Context context, CharSequence title, boolean includeDocuments) { @@ -207,7 +193,7 @@ public static Intent getPickImageChooserIntent(@NonNull Context context, CharSeq * you will be able to get the pictureUri using {@link #getPickImageResultUri(Context, Intent)}. Otherwise, it is just you use * the Uri passed to this method. * - * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. + * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. * @param outputFileUri the Uri where the picture will be placed. */ public static Intent getCameraIntent(@NonNull Context context, Uri outputFileUri) { @@ -223,7 +209,7 @@ public static Intent getCameraIntent(@NonNull Context context, Uri outputFileUri * Get all Camera intents for capturing image using device camera apps. */ public static List getCameraIntents(@NonNull Context context, @NonNull - PackageManager packageManager) { + PackageManager packageManager) { List allIntents = new ArrayList<>(); @@ -249,7 +235,7 @@ public static List getCameraIntents(@NonNull Context context, @NonNull * Get all Gallery intents for getting image from one of the apps of the device that handle images. */ public static List getGalleryIntents(@NonNull - PackageManager packageManager, String action, boolean includeDocuments) { + PackageManager packageManager, String action, boolean includeDocuments) { List intents = new ArrayList<>(); Intent galleryIntent = action == Intent.ACTION_GET_CONTENT ? new Intent(action) : new Intent(action, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); @@ -295,11 +281,11 @@ public static boolean isExplicitCameraPermissionRequired(@NonNull Context contex * @return true - the permission in requested in manifest, false - not. */ public static boolean hasPermissionInManifest(@NonNull Context context, @NonNull - String permissionName) { + String permissionName) { String packageName = context.getPackageName(); try { PackageInfo - packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); final String[] declaredPermisisons = packageInfo.requestedPermissions; if (declaredPermisisons != null && declaredPermisisons.length > 0) { for (String p : declaredPermisisons) { @@ -332,7 +318,7 @@ public static Uri getCaptureImageOutputUri(@NonNull Context context) { * Will return the correct URI for camera and gallery image. * * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. - * @param data the returned data of the activity result + * @param data the returned data of the activity result */ public static Uri getPickImageResultUri(@NonNull Context context, @Nullable Intent data) { boolean isCamera = true; @@ -350,14 +336,14 @@ public static Uri getPickImageResultUri(@NonNull Context context, @Nullable Inte * do we get an exception when we try, Android is awesome. * * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. - * @param uri the result URI of image pick. + * @param uri the result URI of image pick. * @return true - required permission are not granted, false - either no need for permissions or they are granted */ public static boolean isReadExternalStoragePermissionsRequired(@NonNull Context context, @NonNull - Uri uri) { + Uri uri) { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission( - Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && + Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && isUriRequiresPermissions(context, uri); } @@ -366,7 +352,7 @@ public static boolean isReadExternalStoragePermissionsRequired(@NonNull Context * Only relevant for API version 23 and above. * * @param context used to access Android APIs, like content resolve, it is your activity/fragment/widget. - * @param uri the result URI of image pick. + * @param uri the result URI of image pick. */ public static boolean isUriRequiresPermissions(@NonNull Context context, @NonNull Uri uri) { try { @@ -381,7 +367,7 @@ public static boolean isUriRequiresPermissions(@NonNull Context context, @NonNul /** * Create {@link ActivityBuilder} instance to start {@link CropImageActivity} to crop the given image.
- * Result will be recieved in {@link Activity#onActivityResult(int, int, Intent)} and can be retrieved + * Result will be recieved in {@code Activity#onActivityResult(int, int, Intent)} and can be retrieved * using {@link #getActivityResult(Intent)}. * * @param uri the image Android uri source to crop or null to start a picker @@ -394,7 +380,7 @@ public static ActivityBuilder activity(@Nullable Uri uri) { /** * Get {@link CropImageActivity} result data object for crop image activity started using {@link #activity(Uri)}. * - * @param data result data intent as received in {@link Activity#onActivityResult(int, int, Intent)}. + * @param data result data intent as received in {@code Activity#onActivityResult(int, int, Intent)}. * @return Crop Image Activity Result object or null if none exists */ public static ActivityResult getActivityResult(@Nullable Intent data) { @@ -446,41 +432,18 @@ public Intent getIntent(@NonNull Context context, @Nullable Class cls) { /** * Start {@link CropImageActivity}. - * - * @param activity activity to receive result */ - public void start(@NonNull Activity activity) { + public void start(@NonNull Context context, @NonNull ActivityResultLauncher launcher) { mOptions.validate(); - activity.startActivityForResult(getIntent(activity), CROP_IMAGE_ACTIVITY_REQUEST_CODE); + launcher.launch(getIntent(context)); } /** * Start {@link CropImageActivity}. - * - * @param activity activity to receive result */ - public void start(@NonNull Activity activity, @Nullable Class cls) { + public void start(@NonNull Context context, @NonNull ActivityResultLauncher launcher, @Nullable Class cls) { mOptions.validate(); - activity.startActivityForResult(getIntent(activity, cls), CROP_IMAGE_ACTIVITY_REQUEST_CODE); - } - - /** - * Start {@link CropImageActivity}. - * - * @param fragment fragment to receive result - */ - public void start(@NonNull Context context, @NonNull Fragment fragment) { - fragment.startActivityForResult(getIntent(context), CROP_IMAGE_ACTIVITY_REQUEST_CODE); - } - - /** - * Start {@link CropImageActivity}. - * - * @param fragment fragment to receive result - */ - public void start(@NonNull Context context, @NonNull Fragment fragment, @Nullable - Class cls) { - fragment.startActivityForResult(getIntent(context, cls), CROP_IMAGE_ACTIVITY_REQUEST_CODE); + launcher.launch(getIntent(context, cls)); } /** @@ -845,7 +808,7 @@ public ActivityBuilder setRotationDegrees(int rotationDegrees) { * Result data of Crop Image Activity. */ public static final class ActivityResult extends CropImageView.CropResult implements - Parcelable { + Parcelable { public static final Creator CREATOR = new Creator() { @Override diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImageActivity.java b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImageActivity.java index 03b131c..ed901c6 100644 --- a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImageActivity.java +++ b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImageActivity.java @@ -22,14 +22,17 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + import com.linchaolong.android.imagepicker.R; + import java.io.File; import java.io.IOException; @@ -38,7 +41,7 @@ * Use {@link CropImage#activity(Uri)} to create a builder to start this activity. */ public class CropImageActivity extends AppCompatActivity - implements CropImageView.OnSetImageUriCompleteListener, CropImageView.OnCropImageCompleteListener { + implements CropImageView.OnSetImageUriCompleteListener, CropImageView.OnCropImageCompleteListener { /** * The crop image view library widget used in the activity @@ -66,18 +69,21 @@ public void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); mCropImageUri = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_SOURCE); mOptions = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_OPTIONS); + if (mOptions == null) { + mOptions = new CropImageOptions(); + } if (savedInstanceState == null) { if (mCropImageUri == null || mCropImageUri.equals(Uri.EMPTY)) { if (CropImage.isExplicitCameraPermissionRequired(this)) { // request permissions and handle the result in onRequestPermissionsResult() - requestPermissions(new String[]{ Manifest.permission.CAMERA}, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); + requestPermissions(new String[]{Manifest.permission.CAMERA}, CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE); } else { - CropImage.startPickImageActivity(this); + startActivityForResult(CropImage.getPickImageChooserIntent(this), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); } } else if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) { // request permissions and handle the result in onRequestPermissionsResult() - requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); } else { // no permissions required or already grunted, can start crop image activity mCropImageView.setImageUriAsync(mCropImageUri); @@ -169,6 +175,7 @@ public void onBackPressed() { @Override @SuppressLint("NewApi") protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); // handle result of pick image chooser if (requestCode == CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE) { @@ -183,7 +190,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // For API >= 23 we need to check specifically that we have permissions to read external storage. if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) { // request permissions and handle the result in onRequestPermissionsResult() - requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE); } else { // no permissions required or already grunted, can start crop image activity mCropImageView.setImageUriAsync(mCropImageUri); @@ -194,7 +201,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull - int[] grantResults) { + int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE) { if (mCropImageUri != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // required permissions granted, start crop image activity @@ -208,7 +216,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String permissi if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) { //Irrespective of whether camera permission was given or not, we show the picker //The picker will not add the camera intent if permission is not available - CropImage.startPickImageActivity(this); + startActivityForResult(CropImage.getPickImageChooserIntent(this), CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE); } } diff --git a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropOverlayView.java b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropOverlayView.java index 38d1304..71fac89 100644 --- a/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropOverlayView.java +++ b/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropOverlayView.java @@ -690,7 +690,7 @@ private void drawBackground(Canvas canvas) { canvas.save(); canvas.clipPath(mPath, Region.Op.INTERSECT); - canvas.clipRect(rect, Region.Op.XOR); + canvas.clipRect(rect, Region.Op.DIFFERENCE); canvas.drawRect(left, top, right, bottom, mBackgroundPaint); canvas.restore(); } @@ -703,7 +703,7 @@ private void drawBackground(Canvas canvas) { } mPath.addOval(mDrawRect, Path.Direction.CW); canvas.save(); - canvas.clipPath(mPath, Region.Op.XOR); + canvas.clipPath(mPath, Region.Op.DIFFERENCE); canvas.drawRect(left, top, right, bottom, mBackgroundPaint); canvas.restore(); }