From 64dfb82645fa22dd235956a20b4ef4287d6a16b2 Mon Sep 17 00:00:00 2001 From: ShiftHackZ Date: Mon, 12 Sep 2022 21:24:11 +0300 Subject: [PATCH 01/14] Rename .java to .kt --- .../{CameraPickerActivity.java => CameraPickerActivity.kt} | 0 ...leryMultiPickerActivity.java => GalleryMultiPickerActivity.kt} | 0 ...rySinglePickerActivity.java => GallerySinglePickerActivity.kt} | 0 .../shz/imagepicker/imagepicker/{ImagePath.java => ImagePath.kt} | 0 .../{ImagePickerCallback.java => ImagePickerCallback.kt} | 0 .../imagepicker/{ImagePickerDialog.java => ImagePickerDialog.kt} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{CameraPickerActivity.java => CameraPickerActivity.kt} (100%) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{GalleryMultiPickerActivity.java => GalleryMultiPickerActivity.kt} (100%) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{GallerySinglePickerActivity.java => GallerySinglePickerActivity.kt} (100%) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{ImagePath.java => ImagePath.kt} (100%) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{ImagePickerCallback.java => ImagePickerCallback.kt} (100%) rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{ImagePickerDialog.java => ImagePickerDialog.kt} (100%) diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt similarity index 100% rename from imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.java rename to imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt From 24fad1b1d311eea4f6b71b0f5c277854d9d1a80d Mon Sep 17 00:00:00 2001 From: ShiftHackZ Date: Mon, 12 Sep 2022 21:24:11 +0300 Subject: [PATCH 02/14] Migrated library to Kotlin, support api 33 --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 10 + .../imagepickerapp/MainActivity.java | 75 +++---- app/src/main/res/layout/activity_main.xml | 4 - .../src/main/res/xml/provider_path.xml | 0 build.gradle | 9 +- gradle/wrapper/gradle-wrapper.properties | 6 +- imagepicker/build.gradle | 11 +- imagepicker/src/main/AndroidManifest.xml | 13 +- .../imagepicker/CameraPickerActivity.kt | 154 ++++++++------- .../imagepicker/GalleryMultiPickerActivity.kt | 147 ++++++-------- .../GallerySinglePickerActivity.kt | 116 +++++------ .../shz/imagepicker/imagepicker/ImagePath.kt | 184 ++++++++---------- .../imagepicker/imagepicker/ImagePicker.java | 97 --------- .../imagepicker/imagepicker/ImagePicker.kt | 97 +++++++++ .../imagepicker/ImagePickerCallback.kt | 9 +- .../imagepicker/ImagePickerDialog.kt | 97 ++++----- .../imagepicker/MultipleSelectionType.kt | 6 + .../imagepicker/PermissionChecker.kt | 32 +++ .../imagepicker/imagepicker/PickerImage.kt | 18 ++ .../imagepicker/imagepicker/PickerResult.kt | 6 + .../imagepicker/imagepicker/PickerSource.kt | 7 + 22 files changed, 558 insertions(+), 544 deletions(-) rename {imagepicker => app}/src/main/res/xml/provider_path.xml (100%) delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.java create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerImage.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt diff --git a/app/build.gradle b/app/build.gradle index 1301d75..61d97ed 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,13 +3,13 @@ plugins { } android { - compileSdkVersion 31 + compileSdkVersion 32 buildToolsVersion "30.0.3" defaultConfig { applicationId "com.shz.imagepicker.imagepickerapp" minSdkVersion 16 - targetSdkVersion 31 + targetSdkVersion 32 versionCode 1 versionName "1.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bd47d86..1c58b0c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java b/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java index 0c560ce..15a4f1e 100644 --- a/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java +++ b/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java @@ -1,21 +1,17 @@ package com.shz.imagepicker.imagepickerapp; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; -import android.util.Log; import android.view.View; -import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; import com.bumptech.glide.Glide; import com.shz.imagepicker.imagepicker.ImagePicker; import com.shz.imagepicker.imagepicker.ImagePickerCallback; - -import java.io.File; -import java.util.List; +import com.shz.imagepicker.imagepicker.PickerResult; public class MainActivity extends AppCompatActivity implements ImagePickerCallback { @@ -28,6 +24,12 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); mResultImage = (ImageView) findViewById(R.id.iv_result); mResultContainer = (LinearLayout) findViewById(R.id.ll_container); + + findViewById(R.id.btn_gallery).setOnClickListener((v) -> onGalleryClick()); + findViewById(R.id.btn_gallery_multiple).setOnClickListener((v) -> onGalleryMultipleClick()); + findViewById(R.id.btn_camera).setOnClickListener((v) -> onCameraClick()); + findViewById(R.id.btn_generic).setOnClickListener((v) -> onGenericClick()); + findViewById(R.id.btn_generic_multiple).setOnClickListener((v) -> onGenericMultipleClick()); } @Override @@ -36,22 +38,22 @@ protected void onResume() { } @Override - public void onImagesSelected(List files) { - if (files.size() > 0) { - if (files.size() == 1) { - Glide.with(this) - .load(files.get(0)) + public void onImagePickerResult(@NonNull PickerResult result) { + if (result instanceof PickerResult.Single) { + Glide.with(this) + .load(((PickerResult.Single) result).getImage().getFile()) .into(mResultImage); - mResultContainer.setVisibility(View.GONE); - mResultImage.setVisibility(View.VISIBLE); - } else { - mResultContainer.removeAllViews(); - for (int i = 0; i < files.size(); i++) { + mResultContainer.setVisibility(View.GONE); + mResultImage.setVisibility(View.VISIBLE); + } + if (result instanceof PickerResult.Multiple) { + mResultContainer.removeAllViews(); + for (int i = 0; i < ((PickerResult.Multiple) result).getImages().size(); i++) { ImageView imageView = new ImageView(this); Glide.with(this) - .load(files.get(i)) + .load(((PickerResult.Multiple) result).getImages().get(i).getFile()) .into(imageView); mResultContainer.addView(imageView); @@ -60,46 +62,49 @@ public void onImagesSelected(List files) { } mResultContainer.setVisibility(View.VISIBLE); mResultImage.setVisibility(View.GONE); - } } } - public void onCameraClick(View v) { - new ImagePicker.Builder(this, this) + public void onCameraClick() { + getImagePicker() .useCamera(true) .build() - .start(); + .launch(this); } - public void onGalleryMultipleClick(View v) { - new ImagePicker.Builder(this, this) + public void onGalleryMultipleClick() { + getImagePicker() .useGallery(true) - .useMultiSelection(true) + .multipleSelection(true) .build() - .start(); + .launch(this); } - public void onGalleryClick(View v) { - new ImagePicker.Builder(this, this) + public void onGalleryClick() { + getImagePicker() .useGallery(true) .build() - .start(); + .launch(this); } - public void onGenericMultipleClick(View v) { - new ImagePicker.Builder(this, this) + public void onGenericMultipleClick() { + getImagePicker() .useGallery(true) .useCamera(true) - .useMultiSelection(true) + .multipleSelection(true) .build() - .start(); + .launch(this); } - public void onGenericClick(View v) { - new ImagePicker.Builder(this, this) + public void onGenericClick() { + getImagePicker() .useGallery(true) .useCamera(true) .build() - .start(); + .launch(this); + } + + private ImagePicker.Builder getImagePicker() { + return new ImagePicker.Builder(this.getPackageName() + ".provider", this); } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f2d5e70..5ed19e3 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -43,7 +43,6 @@ android:background="#E16666" android:clickable="true" android:focusable="true" - android:onClick="onGalleryClick" android:text="GALLERY" /> @@ -59,7 +58,6 @@ android:background="#E16666" android:clickable="true" android:focusable="true" - android:onClick="onGalleryMultipleClick" android:text="GALLERY MULTI SELECTION" /> - + - - - - \ No newline at end of file diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt index 3a33e9a..4c14355 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt @@ -1,97 +1,105 @@ -package com.shz.imagepicker.imagepicker; +package com.shz.imagepicker.imagepicker -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; -import androidx.core.content.FileProvider; +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import com.shz.imagepicker.imagepicker.ImagePath.getCaptureImageResultUri +import com.shz.imagepicker.imagepicker.ImagePath.getNormalizedUri +import com.shz.imagepicker.imagepicker.ImagePath.getCaptureImageOutputUri +import java.io.File -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.MediaStore; +class CameraPickerActivity : AppCompatActivity() { -import java.io.File; -import java.util.ArrayList; + private var filename: String = "" -public class CameraPickerActivity extends Activity { - - public static ImagePickerCallback mCallback; - - private static final int PERMISSION_REQUEST_CAMERA = 54560; - private static final int IMAGE_REQUEST_CAMERA = 54561; - - private static final String FILE_PROVIDER_PREFIX = ".provider"; - - private String mFilename; + private val cameraLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + getCaptureImageResultUri(this, result.data, filename) + ?.let { uri -> getNormalizedUri(this, uri) } + ?.path + ?.let(::File) + ?.let { file -> PickerImage(PickerSource.CAMERA, file) } + ?.let(PickerResult::Single) + ?.let(callback::onImagePickerResult) + } + finish() + } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == PERMISSION_REQUEST_CAMERA) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startCameraPicker(); + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startCameraPicker() } else { - finish(); + finish() } } } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_REQUEST_CAMERA) { - ArrayList files = new ArrayList<>(); - Uri uri = ImagePath.getCaptureImageResultUri(this, data, mFilename); - Uri uriFile = ImagePath.getNormalizedUri(this, uri); - File file = new File(uriFile.getPath()); - files.add(file); - mCallback.onImagesSelected(files); - } - finish(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - checkCameraPermission(); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkCameraPermission() } - private void checkCameraPermission() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED) { + private fun checkCameraPermission() { + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.CAMERA + ) == PackageManager.PERMISSION_DENIED + ) { ActivityCompat.requestPermissions( - this, - new String[] { Manifest.permission.CAMERA }, - PERMISSION_REQUEST_CAMERA - ); + this, arrayOf(Manifest.permission.CAMERA), + PERMISSION_REQUEST_CAMERA + ) } else { - startCameraPicker(); + startCameraPicker() } } - private void startCameraPicker() { - Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - mFilename = String.valueOf(System.nanoTime()); - Uri uri = ImagePath.getCaptureImageOutputUri(this, mFilename); - if (uri != null) { - File file = new File(uri.getPath()); + private fun startCameraPicker() { + val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + filename = System.nanoTime().toString() + val uri = getCaptureImageOutputUri(this, filename) + if (filename.isNotEmpty()) uri?.path?.let { path -> + val file = File(path) if (Build.VERSION.SDK_INT >= 24) { cameraIntent.putExtra( - MediaStore.EXTRA_OUTPUT, - FileProvider.getUriForFile( - this, - this.getPackageName() + FILE_PROVIDER_PREFIX, - file - ) - ); - cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + MediaStore.EXTRA_OUTPUT, + FileProvider.getUriForFile( + this, + authority, + file + ) + ) + cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } else { - cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri) } - startActivityForResult(cameraIntent, IMAGE_REQUEST_CAMERA); + cameraLauncher.launch(cameraIntent) } } -} \ No newline at end of file + + companion object { + private const val PERMISSION_REQUEST_CAMERA = 54560 + + @JvmField + internal var callback: ImagePickerCallback = ImagePickerCallback { } + internal var authority: String = "" + } +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt index c14fba2..56cfd1e 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt @@ -1,102 +1,77 @@ -package com.shz.imagepicker.imagepicker; +package com.shz.imagepicker.imagepicker -import android.Manifest; -import android.app.Activity; -import android.content.ClipData; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.MediaStore; -import android.util.Log; +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import com.shz.imagepicker.imagepicker.ImagePath.getImagePathFromInputStreamUri +import java.io.File -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; +class GalleryMultiPickerActivity : AppCompatActivity() { -import java.io.File; -import java.util.ArrayList; - -public class GalleryMultiPickerActivity extends Activity { - - public static ImagePickerCallback mCallback; - - private static final int PERMISSION_REQUEST_READ_STORAGE = 54564; - private static final int IMAGE_REQUEST_GALLERY = 54565; - - private static final String GALLERY_IMAGE_MIME = "image/jpeg"; + private val galleryLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val output = arrayListOf() + result.data?.clipData?.let { clipData -> + for (i in 0 until clipData.itemCount) { + getImagePathFromInputStreamUri(this, clipData.getItemAt(i).uri) + ?.let(::File) + ?.takeIf(File::exists) + ?.let { file -> PickerImage(PickerSource.GALLERY_MULTIPLE, file) } + ?.let(output::add) + } + } + when (output.size) { + 0 -> null + 1 -> PickerResult.Single(output.first()) + else -> PickerResult.Multiple(output) + }?.let(callback::onImagePickerResult) + } + finish() + } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST_READ_STORAGE) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startGalleryPicker(); + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_CHECK_REQUEST) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startGalleryPicker() } else { - finish(); + finish() } } } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - checkGalleryPermission(); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkGalleryPermission(::startGalleryPicker) } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_REQUEST_GALLERY) { - if (data != null && data.getData() != null) { - ArrayList files = new ArrayList<>(); - - /*if (data.getData() != null) { - Log.d("MultiPicker", "data: " + data.getData().toString()); - String filename = ImagePath.getImagePathFromInputStreamUri(this, data.getData()); - File file = new File(filename); - if (file.exists()) { - files.add(file); - } - }*/ - if (data.getClipData() != null) { - ClipData clipData = data.getClipData(); - - for (int i = 0; i < clipData.getItemCount(); i++) { - ClipData.Item item = clipData.getItemAt(i); - String filename = ImagePath.getImagePathFromInputStreamUri(this, item.getUri()); - File file = new File(filename); - if (file.exists()) { - files.add(file); - } - } - } - - mCallback.onImagesSelected(files); + private fun startGalleryPicker() { + galleryLauncher.launch( + Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply { + if (Build.VERSION.SDK_INT >= 18) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + setDataAndType( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + GALLERY_IMAGE_MIME + ) } - } - finish(); + ) } - private void checkGalleryPermission() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED - || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - ActivityCompat.requestPermissions( - this, - new String[] { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, - PERMISSION_REQUEST_READ_STORAGE - ); - } else { - startGalleryPicker(); - } - } + companion object { + private const val GALLERY_IMAGE_MIME = "image/jpeg" - private void startGalleryPicker() { - Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - if (Build.VERSION.SDK_INT >= 18) { - galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - galleryIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, GALLERY_IMAGE_MIME); - startActivityForResult(galleryIntent, IMAGE_REQUEST_GALLERY); + @JvmField + internal var callback: ImagePickerCallback = ImagePickerCallback { } } -} \ No newline at end of file +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt index e22f262..2010fe9 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt @@ -1,80 +1,68 @@ -package com.shz.imagepicker.imagepicker; +package com.shz.imagepicker.imagepicker -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.provider.MediaStore; -import android.util.Log; +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.provider.MediaStore +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import com.shz.imagepicker.imagepicker.ImagePath.getImagePathFromInputStreamUri +import java.io.File -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; +class GallerySinglePickerActivity : AppCompatActivity() { -import java.io.File; -import java.util.ArrayList; - -public class GallerySinglePickerActivity extends Activity { - - public static ImagePickerCallback mCallback; - - private static final int PERMISSION_REQUEST_READ_STORAGE = 54562; - private static final int IMAGE_REQUEST_GALLERY = 54563; - - private static final String GALLERY_IMAGE_MIME = "image/jpeg"; - - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST_READ_STORAGE) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startGalleryPicker(); - } else { - finish(); - } + private val galleryLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + result.data + ?.data + ?.let { uri -> getImagePathFromInputStreamUri(this, uri) } + ?.let(::File) + ?.takeIf(File::exists) + ?.let { file -> PickerImage(PickerSource.GALLERY_SINGLE, file) } + ?.let(PickerResult::Single) + ?.let(callback::onImagePickerResult) } + finish() } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_REQUEST_GALLERY) { - if (data != null && data.getData() != null) { - ArrayList files = new ArrayList<>(); - String filename = ImagePath.getImagePathFromInputStreamUri(this, data.getData()); - Log.d("SinglePicker", "data: " + data.getData().toString()); - File file = new File(filename); - if (file.exists()) { - files.add(file); - mCallback.onImagesSelected(files); - } + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_CHECK_REQUEST) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startGalleryPicker() + } else { + finish() } } - finish(); } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - checkGalleryPermission(); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkGalleryPermission(::startGalleryPicker) } - private void checkGalleryPermission() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED - || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - ActivityCompat.requestPermissions( - this, - new String[] { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, - PERMISSION_REQUEST_READ_STORAGE - ); - } else { - startGalleryPicker(); - } + private fun startGalleryPicker() { + galleryLauncher.launch( + Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply { + setDataAndType( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + GALLERY_IMAGE_MIME + ) + } + ) } - private void startGalleryPicker() { - Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - galleryIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, GALLERY_IMAGE_MIME); - startActivityForResult(galleryIntent, IMAGE_REQUEST_GALLERY); + companion object { + private const val GALLERY_IMAGE_MIME = "image/jpeg" + + @JvmField + internal var callback: ImagePickerCallback = ImagePickerCallback { } } } \ No newline at end of file diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt index c64511b..042724a 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt @@ -1,124 +1,110 @@ -package com.shz.imagepicker.imagepicker; - -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.provider.MediaStore; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Calendar; - -public class ImagePath { - - private static final String FILE_EXTENSION_JPEG = ".jpeg"; - private static final String FILE_EXTENSION_JPG = ".jpg"; - private static final String FILE_SCHEMA_CONTENT = "content:"; - - public static Uri getCaptureImageOutputUri(Context context, String filename) { - Uri outputUri = null; - File imageStore = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); - if (imageStore != null) { - outputUri = Uri.fromFile(new File(imageStore.getPath(), filename + FILE_EXTENSION_JPEG)); - } - return outputUri; - } - - public static Uri getCaptureImageResultUri(Context context, Intent data, String filename) { - boolean isCamera = true; - if (data != null && data.getData() != null) { - String action = data.getAction(); - isCamera = action != null && action == MediaStore.ACTION_IMAGE_CAPTURE; - } - if (isCamera || data.getData() == null) { - return getCaptureImageOutputUri(context, filename); - } else { - return data.getData(); +package com.shz.imagepicker.imagepicker + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Environment +import android.provider.MediaStore +import com.shz.imagepicker.imagepicker.ImagePath +import java.io.* +import java.lang.Exception +import java.util.* +import kotlin.Throws + +object ImagePath { + private const val FILE_EXTENSION_JPEG = ".jpeg" + private const val FILE_EXTENSION_JPG = ".jpg" + private const val FILE_SCHEMA_CONTENT = "content:" + + @JvmStatic + fun getCaptureImageOutputUri(context: Context?, filename: String): Uri? = context + ?.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + ?.let { file -> Uri.fromFile(File(file.path, filename + FILE_EXTENSION_JPEG)) } + + @JvmStatic + fun getCaptureImageResultUri(context: Context, data: Intent?, filename: String): Uri? { + var isCamera = true + if (data != null && data.data != null) { + val action = data.action + isCamera = action != null && action === MediaStore.ACTION_IMAGE_CAPTURE } + return if (isCamera || data!!.data == null) getCaptureImageOutputUri(context, filename) + else data.data } - public static Uri getNormalizedUri(Context context, Uri uri) { - if (uri != null && uri.toString().contains(FILE_SCHEMA_CONTENT)) { - return Uri.fromFile(getPath(context, uri, MediaStore.Images.Media.DATA)); - } else { - return uri; - } - } - - public static String getImagePathFromInputStreamUri(Context context, Uri uri) { - InputStream inputStream = null; - String filePath = null; - - if (uri.getAuthority() != null) { + @JvmStatic + fun getNormalizedUri(context: Context, uri: Uri?): Uri? = + if (uri != null && uri.toString().contains(FILE_SCHEMA_CONTENT)) + Uri.fromFile( + getPath( + context, + uri, + MediaStore.Images.Media.DATA + ) + ) + else uri + + @JvmStatic + fun getImagePathFromInputStreamUri(context: Context, uri: Uri): String? { + var inputStream: InputStream? = null + var filePath: String? = null + if (uri.authority != null) { try { - inputStream = context.getContentResolver().openInputStream(uri); - File photoFile = createTempFileFrom(context, inputStream); - filePath = photoFile.getPath(); - } catch (FileNotFoundException ex) { - ex.printStackTrace(); - } catch (Exception ex) { - ex.printStackTrace(); + inputStream = context.contentResolver.openInputStream(uri) + val photoFile = createTempFileFrom(context, inputStream) + filePath = photoFile!!.path + } catch (ex: FileNotFoundException) { + ex.printStackTrace() + } catch (ex: Exception) { + ex.printStackTrace() } finally { try { - inputStream.close(); - } catch (IOException ex) { - ex.printStackTrace(); + inputStream!!.close() + } catch (ex: IOException) { + ex.printStackTrace() } } } - return filePath; + return filePath } - private static File createTempFileFrom(Context context, InputStream inputStream) throws IOException { - File targetFile = null; - + @Throws(IOException::class) + private fun createTempFileFrom(context: Context, inputStream: InputStream?): File? { + var targetFile: File? = null if (inputStream != null) { - int read; - byte[] buffer = new byte[8 * 1024]; - - targetFile = createTempFile(context, null); - FileOutputStream outputStream = new FileOutputStream(targetFile); - + var read: Int + val buffer = ByteArray(8 * 1024) + targetFile = createTempFile(context, null) + val outputStream = FileOutputStream(targetFile) while (true) { - read = inputStream.read(buffer); + read = inputStream.read(buffer) if (read == -1) { - break; + break } - outputStream.write(buffer, 0, read); + outputStream.write(buffer, 0, read) } - outputStream.flush(); - + outputStream.flush() try { - outputStream.close(); - } catch (IOException ex) { - ex.printStackTrace(); + outputStream.close() + } catch (ex: IOException) { + ex.printStackTrace() } } - return targetFile; + return targetFile } - private static File createTempFile(Context context, String filePath) { - String tempFilename; - if (filePath == null) { - tempFilename = String.valueOf(Calendar.getInstance().getTimeInMillis()); - } else { - tempFilename = filePath; - } - return new File(context.getExternalCacheDir(), tempFilename + FILE_EXTENSION_JPG); + private fun createTempFile(context: Context, filePath: String?): File { + val tempFilename: String = filePath ?: Calendar.getInstance().timeInMillis.toString() + return File(context.externalCacheDir, tempFilename + FILE_EXTENSION_JPG) } - private static File getPath(Context context, Uri uri, String column) { - String[] columns = { column }; - Cursor cursor = context.getContentResolver().query(uri, columns, null, null, null); - int columnIndex = cursor.getColumnIndexOrThrow(column); - cursor.moveToFirst(); - String path = cursor.getString(columnIndex); - cursor.close(); - return new File(path); + private fun getPath(context: Context, uri: Uri, column: String): File { + val columns = arrayOf(column) + val cursor = context.contentResolver.query(uri, columns, null, null, null) + val columnIndex = cursor!!.getColumnIndexOrThrow(column) + cursor.moveToFirst() + val path = cursor.getString(columnIndex) + cursor.close() + return File(path) } } diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.java b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.java deleted file mode 100644 index c03ac0d..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.shz.imagepicker.imagepicker; - -import android.app.Activity; -import android.content.Intent; - -public class ImagePicker { - - private static final String IMAGE_PICKER_DIALOG = "ImagePicker"; - - private Activity mActivity; - private ImagePickerCallback mCallback; - private boolean mIsUsingCamera = false; - private boolean mIsUsingGallery = false; - private boolean mIsUsingGalleryMultiSelect = false; - - private ImagePicker() { } - - public static class Builder { - private final ImagePicker mPicker; - - public Builder(Activity activity, ImagePickerCallback callback) { - mPicker = new ImagePicker(); - mPicker.mActivity = activity; - mPicker.mCallback = callback; - } - - public Builder useCamera(boolean isUsingCamera) { - mPicker.mIsUsingCamera = isUsingCamera; - return this; - } - - public Builder useGallery(boolean isUsingGallery) { - mPicker.mIsUsingGallery = isUsingGallery; - return this; - } - - public Builder useMultiSelection(boolean isUsingMultiSelection) { - mPicker.mIsUsingGalleryMultiSelect = isUsingMultiSelection; - return this; - } - - public ImagePicker build() { - return mPicker; - } - } - - public void start() { - if (mIsUsingCamera && !mIsUsingGallery) { - startCamera(); - } else if (mIsUsingGallery && !mIsUsingGalleryMultiSelect && !mIsUsingCamera) { - startGallerySingle(); - } else if (mIsUsingGallery && mIsUsingGalleryMultiSelect && !mIsUsingCamera) { - startGalleryMultiple(); - } else if (mIsUsingGallery && !mIsUsingGalleryMultiSelect && mIsUsingCamera) { - ImagePickerDialog dialog = new ImagePickerDialog(new ImagePickerDialog.ImagePickerDialogListener() { - @Override - public void onCamera() { - startCamera(); - } - - @Override - public void onGallery() { - startGallerySingle(); - } - }); - dialog.show(mActivity.getFragmentManager(), IMAGE_PICKER_DIALOG); - } else if (mIsUsingGallery && mIsUsingGalleryMultiSelect && mIsUsingCamera) { - ImagePickerDialog dialog = new ImagePickerDialog(new ImagePickerDialog.ImagePickerDialogListener() { - @Override - public void onCamera() { - startCamera(); - } - - @Override - public void onGallery() { - startGalleryMultiple(); - } - }); - dialog.show(mActivity.getFragmentManager(), IMAGE_PICKER_DIALOG); - } - } - - private void startCamera() { - CameraPickerActivity.mCallback = mCallback; - mActivity.startActivity(new Intent(mActivity, CameraPickerActivity.class)); - } - - private void startGallerySingle() { - GallerySinglePickerActivity.mCallback = mCallback; - mActivity.startActivity(new Intent(mActivity, GallerySinglePickerActivity.class)); - } - - private void startGalleryMultiple() { - GalleryMultiPickerActivity.mCallback = mCallback; - mActivity.startActivity(new Intent(mActivity, GalleryMultiPickerActivity.class)); - } -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt new file mode 100644 index 0000000..3cb8bc5 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt @@ -0,0 +1,97 @@ +@file:Suppress("unused") + +package com.shz.imagepicker.imagepicker + +import android.content.Context +import android.content.Intent + +class ImagePicker private constructor( + private val authority: String, + private val callback: ImagePickerCallback = ImagePickerCallback { }, + private val useCamera: Boolean = true, + private val useGallery: Boolean = false, + private val multipleSelection: Boolean = false, + private val multipleSelectionType: MultipleSelectionType = MultipleSelectionType.NATIVE, +) { + + fun launch(context: Context) { + when { + useCamera && !useGallery -> { + launchCameraPicker(context) + } + useGallery && !multipleSelection && !useCamera -> { + launchSingleSelectionGallery(context) + } + useGallery && multipleSelection && !useCamera -> when (multipleSelectionType) { + MultipleSelectionType.NATIVE -> launchMultipleSelectionGalleryNative(context) + MultipleSelectionType.CUSTOM -> launchMultipleSelectionGalleryCustom(context) + } + else -> Unit + } + } + + private fun launchCameraPicker(context: Context) { + CameraPickerActivity.callback = callback + CameraPickerActivity.authority = authority + context.startActivity(Intent(context, CameraPickerActivity::class.java)) + } + + private fun launchSingleSelectionGallery(context: Context) { + GallerySinglePickerActivity.callback = callback + context.startActivity(Intent(context, GallerySinglePickerActivity::class.java)) + } + + private fun launchMultipleSelectionGalleryNative(context: Context) { + GalleryMultiPickerActivity.callback = callback + context.startActivity(Intent(context, GalleryMultiPickerActivity::class.java)) + } + + private fun launchMultipleSelectionGalleryCustom(context: Context) { + //ToDo: implement me + } + + class Builder( + private val authority: String, + private val callback: ImagePickerCallback, + ) { + private var useCamera: Boolean = false + private var useGallery: Boolean = false + private var multipleSelection: Boolean = false + private var multipleSelectionType: MultipleSelectionType = MultipleSelectionType.NATIVE + + fun build(): ImagePicker = ImagePicker( + authority, + callback, + useCamera, + useGallery, + multipleSelection, + ) + + fun useCamera(useCamera: Boolean = true) = apply { + this.useCamera = useCamera + } + + fun useGallery(useGallery: Boolean = true) = apply { + this.useGallery = useGallery + } + + fun multipleSelection(multipleSelection: Boolean) = apply { + this.multipleSelection = multipleSelection + } + + fun multipleSelectionType(multipleSelectionType: MultipleSelectionType) = apply { + this.multipleSelectionType = multipleSelectionType + } + + companion object { + + inline fun build( + authority: String, + callback: ImagePickerCallback, + block: Builder.() -> Unit, + ) = Builder(authority, callback) + .apply(block) + .build() + } + } +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt index 9e73674..a2a3506 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt @@ -1,8 +1,5 @@ -package com.shz.imagepicker.imagepicker; +package com.shz.imagepicker.imagepicker -import java.io.File; -import java.util.List; - -public interface ImagePickerCallback { - void onImagesSelected(List files); +fun interface ImagePickerCallback { + fun onImagePickerResult(result: PickerResult) } diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt index 326d1dd..3a17735 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt @@ -1,66 +1,55 @@ -package com.shz.imagepicker.imagepicker; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.app.DialogFragment; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.LinearLayout; - -@SuppressLint("ValidFragment") -public class ImagePickerDialog extends DialogFragment { - - public interface ImagePickerDialogListener { - void onCamera(); - void onGallery(); - } - - private ImagePickerDialogListener mListener; - - public ImagePickerDialog(ImagePickerDialogListener listener) { - super(); - this.mListener = listener; +package com.shz.imagepicker.imagepicker + +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.widget.LinearLayout +import androidx.fragment.app.DialogFragment + +class ImagePickerDialog(private val listener: ImagePickerDialogListener) : DialogFragment() { + + interface ImagePickerDialogListener { + fun onCamera() + fun onGallery() } - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Dialog dialog = super.onCreateDialog(savedInstanceState); - dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); - return dialog; + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) + return dialog } - @Override - public void onStart() { - super.onStart(); - Dialog dialog = getDialog(); + override fun onStart() { + super.onStart() + val dialog = dialog if (dialog != null) { //dialog.getWindow().setLayout(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - WindowManager.LayoutParams params = dialog.getWindow().getAttributes(); + val params = dialog.window!!.attributes //params.horizontalMargin = 0.01f; - dialog.getWindow().setAttributes(params); - - dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE)); + dialog.window!!.attributes = params + dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.WHITE)) } } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.layout_image_picker, container, false); - ((LinearLayout) rootView.findViewById(R.id.btn_camera)).setOnClickListener(v -> { - mListener.onCamera(); - dismissAllowingStateLoss(); - }); - ((LinearLayout) rootView.findViewById(R.id.btn_gallery)).setOnClickListener(v -> { - mListener.onGallery(); - dismissAllowingStateLoss(); - }); - return rootView; + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val rootView = inflater.inflate(R.layout.layout_image_picker, container, false) + (rootView.findViewById(R.id.btn_camera) as LinearLayout).setOnClickListener { + listener.onCamera() + dismissAllowingStateLoss() + } + (rootView.findViewById(R.id.btn_gallery) as LinearLayout).setOnClickListener { + listener.onGallery() + dismissAllowingStateLoss() + } + return rootView } } diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt new file mode 100644 index 0000000..1941407 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt @@ -0,0 +1,6 @@ +package com.shz.imagepicker.imagepicker + +enum class MultipleSelectionType { + NATIVE, + CUSTOM; +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt new file mode 100644 index 0000000..dbb818b --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt @@ -0,0 +1,32 @@ +package com.shz.imagepicker.imagepicker + +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat + +const val PERMISSION_CHECK_REQUEST = 54567 + +fun Activity.checkGalleryPermission(action: () -> Unit) { + val hasNoReadPermission = ContextCompat.checkSelfPermission( + this, + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_DENIED + + val hasNoWritePermission = ContextCompat.checkSelfPermission( + this, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_DENIED + + if (hasNoReadPermission || hasNoWritePermission) + ActivityCompat.requestPermissions( + this, + arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + ), + PERMISSION_CHECK_REQUEST, + ) + else action() +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerImage.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerImage.kt new file mode 100644 index 0000000..e70a683 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerImage.kt @@ -0,0 +1,18 @@ +package com.shz.imagepicker.imagepicker + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import java.io.File + +data class PickerImage( + val source: PickerSource, + val file: File, +) { + val bitmap: Bitmap + get() = BitmapFactory.decodeFile(file.path) + + val bitmapDrawable: Drawable? + get() = BitmapDrawable.createFromPath(file.path) +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt new file mode 100644 index 0000000..5c33127 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt @@ -0,0 +1,6 @@ +package com.shz.imagepicker.imagepicker + +sealed class PickerResult { + data class Single(val image: PickerImage) : PickerResult() + data class Multiple(val images: List) : PickerResult() +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt new file mode 100644 index 0000000..4aae1a4 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt @@ -0,0 +1,7 @@ +package com.shz.imagepicker.imagepicker + +enum class PickerSource { + GALLERY_SINGLE, + GALLERY_MULTIPLE, + CAMERA; +} From 75d5f3231946abc5d5070e80266d1d01f039622c Mon Sep 17 00:00:00 2001 From: ShiftHackZ Date: Tue, 13 Sep 2022 19:03:16 +0300 Subject: [PATCH 03/14] Implemented custom gallery selector --- README.md | 108 ++++++---- app/build.gradle | 20 +- .../imagepickerapp/ImagesDemoAdapter.kt | 47 ++++ .../imagepickerapp/MainActivity.java | 110 ---------- .../imagepickerapp/MainActivity.kt | 85 ++++++++ app/src/main/res/drawable/ic_camera.xml | 9 + app/src/main/res/drawable/ic_custom.xml | 9 + app/src/main/res/drawable/ic_gallery.xml | 9 + app/src/main/res/drawable/ic_list.xml | 9 + app/src/main/res/layout/activity_main.xml | 203 ++++++++++-------- app/src/main/res/layout/item_demo_image.xml | 22 ++ imagepicker/build.gradle | 5 +- imagepicker/src/main/AndroidManifest.xml | 19 +- .../imagepicker/CameraPickerActivity.kt | 105 --------- .../imagepicker/GalleryMultiPickerActivity.kt | 77 ------- .../GallerySinglePickerActivity.kt | 68 ------ .../shz/imagepicker/imagepicker/ImagePath.kt | 110 ---------- .../imagepicker/imagepicker/ImagePicker.kt | 134 +++++++----- .../imagepicker/ImagePickerCallback.kt | 4 +- .../imagepicker/ImagePickerDialog.kt | 55 ----- .../imagepicker/ImagePickerLauncher.kt | 95 ++++++++ .../imagepicker/MultipleSelectionType.kt | 6 - .../imagepicker/PermissionChecker.kt | 32 --- .../imagepicker/imagepicker/PickerResult.kt | 6 - .../imagepicker/imagepicker/PickerSource.kt | 7 - .../activity/camera/CameraPickerActivity.kt | 70 ++++++ .../GalleryPickerCustomActivity.kt | 110 ++++++++++ .../multiple/GalleryImagesMultipleAdapter.kt | 60 ++++++ .../single/GalleryImagesSingleAdapter.kt | 36 ++++ .../customgallery/view/GalleryImageView.kt | 32 +++ .../activity/dialog/DialogLauncherActivity.kt | 73 +++++++ .../activity/dialog/ImagePickerDialog.kt | 53 +++++ .../GalleryMultiPickerNativeActivity.kt | 62 ++++++ .../GallerySinglePickerActivity.kt | 53 +++++ .../imagepicker/core/ImagePickerActivity.kt | 47 ++++ .../exception/NothingToLaunchException.kt | 3 + .../exception/UnableLaunchDialogException.kt | 3 + .../imagepicker/model/GallerySelector.kt | 8 + .../{PickerImage.kt => model/PickedImage.kt} | 13 +- .../imagepicker/model/PickedResult.kt | 10 + .../imagepicker/model/PickedSource.kt | 10 + .../imagepicker/utils/CustomGalleryUtils.kt | 58 +++++ .../imagepicker/utils/ImagePathUtils.kt | 109 ++++++++++ .../imagepicker/utils/PermissionUtils.kt | 38 ++++ .../res/drawable/selector_picker_gallery.xml | 5 + .../image_picker_activity_gallery_custom.xml | 71 ++++++ ....xml => image_picker_dialog_selection.xml} | 8 +- ...age_picker_item_gallery_image_multiple.xml | 24 +++ ...image_picker_item_gallery_image_single.xml | 16 ++ imagepicker/src/main/res/values/strings.xml | 12 +- imagepicker/src/main/res/values/styles.xml | 13 +- 51 files changed, 1568 insertions(+), 783 deletions(-) create mode 100644 app/src/main/java/com/shz/imagepicker/imagepickerapp/ImagesDemoAdapter.kt delete mode 100644 app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java create mode 100644 app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.kt create mode 100644 app/src/main/res/drawable/ic_camera.xml create mode 100644 app/src/main/res/drawable/ic_custom.xml create mode 100644 app/src/main/res/drawable/ic_gallery.xml create mode 100644 app/src/main/res/drawable/ic_list.xml create mode 100644 app/src/main/res/layout/item_demo_image.xml delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerLauncher.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt delete mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/camera/CameraPickerActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/GalleryPickerCustomActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/adapter/multiple/GalleryImagesMultipleAdapter.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/adapter/single/GalleryImagesSingleAdapter.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/view/GalleryImageView.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/dialog/DialogLauncherActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/dialog/ImagePickerDialog.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/nativegallery/GalleryMultiPickerNativeActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/nativegallery/GallerySinglePickerActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/core/ImagePickerActivity.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/exception/NothingToLaunchException.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/exception/UnableLaunchDialogException.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/model/GallerySelector.kt rename imagepicker/src/main/java/com/shz/imagepicker/imagepicker/{PickerImage.kt => model/PickedImage.kt} (67%) create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/model/PickedResult.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/model/PickedSource.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/utils/CustomGalleryUtils.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/utils/ImagePathUtils.kt create mode 100644 imagepicker/src/main/java/com/shz/imagepicker/imagepicker/utils/PermissionUtils.kt create mode 100644 imagepicker/src/main/res/drawable/selector_picker_gallery.xml create mode 100644 imagepicker/src/main/res/layout/image_picker_activity_gallery_custom.xml rename imagepicker/src/main/res/layout/{layout_image_picker.xml => image_picker_dialog_selection.xml} (88%) create mode 100644 imagepicker/src/main/res/layout/image_picker_item_gallery_image_multiple.xml create mode 100644 imagepicker/src/main/res/layout/image_picker_item_gallery_image_single.xml diff --git a/README.md b/README.md index f8e891c..93e670d 100644 --- a/README.md +++ b/README.md @@ -9,64 +9,100 @@ Android library that can be used as quick solution to ImagePicker feature implem - Camera photo picker - Gallery single photo picker - Gallery multiple photo picker +- Custom gallery picker, supports multiple selection (for old non-AOSP Android ROMs that does not support multiple selection intent) ## Implementation 1. In project-level gradle add new maven repository: -
+```groovy
 allprojects {
     repositories {
-        ...
         maven { url 'https://jitpack.io' }
     }
 }
-
+``` 2. In app-level gradle add new implementation: -
+```groovy
 dependencies {
-    implementation 'com.github.ShiftHackZ:ImagePicker:v1.0'
+    implementation 'com.github.ShiftHackZ:ImagePicker:v2.0'
 }
-
- -3. In order to receive images, implement ImagePickerCallback in your Fragment/Activity or as object: - -
-public class MainActivity extends AppCompatActivity implements ImagePickerCallback {
-    ...
-    @Override
-    public void onImagesSelected(List files) {
-        // Do whatever you want with list of files
-        for (int i = 0; i < files.size(); i++) {
-            // As example you can process each file inside for-cycle
-        }        
-    }    
-    ...
+```
+
+3. Create file `provider_path.xml` in `res/xml` folder:
+
+```xml
+
+
+    
+    
+
+```
+
+4. In your `AndroidManifest.xml` add the file provider inside the `
+    
+
+```
+
+5. In order to receive images, implement `ImagePickerCallback` in your Fragment/Activity or as object:
+
+```kotlin
+class MainActivity : AppCompatActivity(), ImagePickerCallback {
+
+    override fun onImagePickerResult(result: PickedResult) {
+        when (result) {
+            PickedResult.Empty -> {
+                // No file was selected, noting to do
+            }
+            is PickedResult.Error -> {
+                val throwable = result.throwable
+                // Some error happened, handle this throwable
+            }
+            is PickedResult.Multiple -> {
+                val pickedImages = result.images
+                val files = pickedImages.map { it.file }
+                // Selected multiple images, do whatever you want with files
+            }
+            is PickedResult.Single -> {
+                val pickedImage = result.image
+                val file = pickedImage.file
+                // Selected one image, do whatever you want with file
+            }
+        }
+    }
 }
-
+``` -4. Create an instance of ImagePicker using ImagePicker.Builder(), which require 2 mandatory params: current Activity and ImagePickerCallback: +6. Create an instance of ImagePicker using ImagePicker.Builder(), which require 2 mandatory params: current Activity and ImagePickerCallback: -
-ImagePicker imagePicker = new ImagePicker.Builder(activity, callback)
-    .useGallery(true)
-    .useCamera(true)
-    .useMultiSelection(true)
-    .build();
-
+```kotlin +val imagePicker = ImagePicker.Builder(this.packageName + ".provider", this) + .useGallery(true) // Use gallery picker if true + .useCamera(true) // Use camera picker if true + .multipleSelection() // Allow multiple selection in gallery picker + .minimumSelectionCount(2) // Defines min count of GallerySelector.CUSTOM multiple selection gallery picker + .maximumSelectionCount(3) // Defines max count of GallerySelector.CUSTOM multiple selection gallery picker + .gallerySelector(GallerySelector.CUSTOM) // Available values: GallerySelector.NATIVE, GallerySelector.CUSTOM + .build() +``` -List of Builder methods: -- useGallery(boolean) // Pass 'true' if you want to enable gallery picker -- useMultiSelection(boolean) // Pass 'true' if you need gallery picker to support multiple photo selection -- useCamera(boolean) // Pass 'true' if you want to enable camera picker 5. Finally, launch your ImagePicker: -
-imagePicker.start();
-
+```kotlin +imagePicker.launch(context) +``` ## Credits - Developer: Dmitriy Moroz diff --git a/app/build.gradle b/app/build.gradle index 61d97ed..8f6de73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,16 @@ plugins { id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' } android { - compileSdkVersion 32 - buildToolsVersion "30.0.3" + compileSdkVersion 33 defaultConfig { applicationId "com.shz.imagepicker.imagepickerapp" minSdkVersion 16 - targetSdkVersion 32 + targetSdkVersion 33 versionCode 1 versionName "1.0" @@ -21,6 +22,9 @@ android { minifyEnabled false } } + buildFeatures { + dataBinding true + } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -29,9 +33,9 @@ android { dependencies { implementation project(':imagepicker') - implementation 'com.github.bumptech.glide:glide:4.12.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + implementation 'com.github.bumptech.glide:glide:4.13.2' + kapt 'com.github.bumptech.glide:compiler:4.13.2' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' } \ No newline at end of file diff --git a/app/src/main/java/com/shz/imagepicker/imagepickerapp/ImagesDemoAdapter.kt b/app/src/main/java/com/shz/imagepicker/imagepickerapp/ImagesDemoAdapter.kt new file mode 100644 index 0000000..502ae05 --- /dev/null +++ b/app/src/main/java/com/shz/imagepicker/imagepickerapp/ImagesDemoAdapter.kt @@ -0,0 +1,47 @@ +package com.shz.imagepicker.imagepickerapp + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.shz.imagepicker.imagepicker.model.PickedImage +import com.shz.imagepicker.imagepickerapp.databinding.ItemDemoImageBinding + +class ImagesDemoAdapter : ListAdapter(diff) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemDemoImageBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + inner class ViewHolder( + private val binding: ItemDemoImageBinding, + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: PickedImage) { + Glide.with(binding.image) + .load(item.file) + .centerCrop() + .into(binding.image) + } + } + + companion object { + private val diff = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: PickedImage, + newItem: PickedImage + ): Boolean = oldItem == newItem + + override fun areContentsTheSame( + oldItem: PickedImage, + newItem: PickedImage + ): Boolean = oldItem == newItem + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java b/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java deleted file mode 100644 index 15a4f1e..0000000 --- a/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.shz.imagepicker.imagepickerapp; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.bumptech.glide.Glide; -import com.shz.imagepicker.imagepicker.ImagePicker; -import com.shz.imagepicker.imagepicker.ImagePickerCallback; -import com.shz.imagepicker.imagepicker.PickerResult; - -public class MainActivity extends AppCompatActivity implements ImagePickerCallback { - - private ImageView mResultImage; - private LinearLayout mResultContainer; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - mResultImage = (ImageView) findViewById(R.id.iv_result); - mResultContainer = (LinearLayout) findViewById(R.id.ll_container); - - findViewById(R.id.btn_gallery).setOnClickListener((v) -> onGalleryClick()); - findViewById(R.id.btn_gallery_multiple).setOnClickListener((v) -> onGalleryMultipleClick()); - findViewById(R.id.btn_camera).setOnClickListener((v) -> onCameraClick()); - findViewById(R.id.btn_generic).setOnClickListener((v) -> onGenericClick()); - findViewById(R.id.btn_generic_multiple).setOnClickListener((v) -> onGenericMultipleClick()); - } - - @Override - protected void onResume() { - super.onResume(); - } - - @Override - public void onImagePickerResult(@NonNull PickerResult result) { - if (result instanceof PickerResult.Single) { - Glide.with(this) - .load(((PickerResult.Single) result).getImage().getFile()) - .into(mResultImage); - - mResultContainer.setVisibility(View.GONE); - mResultImage.setVisibility(View.VISIBLE); - } - if (result instanceof PickerResult.Multiple) { - mResultContainer.removeAllViews(); - for (int i = 0; i < ((PickerResult.Multiple) result).getImages().size(); i++) { - ImageView imageView = new ImageView(this); - - Glide.with(this) - .load(((PickerResult.Multiple) result).getImages().get(i).getFile()) - .into(imageView); - - mResultContainer.addView(imageView); - imageView.getLayoutParams().height = 300; - imageView.requestLayout(); - } - mResultContainer.setVisibility(View.VISIBLE); - mResultImage.setVisibility(View.GONE); - } - } - - public void onCameraClick() { - getImagePicker() - .useCamera(true) - .build() - .launch(this); - } - - public void onGalleryMultipleClick() { - getImagePicker() - .useGallery(true) - .multipleSelection(true) - .build() - .launch(this); - } - - public void onGalleryClick() { - getImagePicker() - .useGallery(true) - .build() - .launch(this); - } - - public void onGenericMultipleClick() { - getImagePicker() - .useGallery(true) - .useCamera(true) - .multipleSelection(true) - .build() - .launch(this); - } - - public void onGenericClick() { - getImagePicker() - .useGallery(true) - .useCamera(true) - .build() - .launch(this); - } - - private ImagePicker.Builder getImagePicker() { - return new ImagePicker.Builder(this.getPackageName() + ".provider", this); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.kt b/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.kt new file mode 100644 index 0000000..30d494e --- /dev/null +++ b/app/src/main/java/com/shz/imagepicker/imagepickerapp/MainActivity.kt @@ -0,0 +1,85 @@ +package com.shz.imagepicker.imagepickerapp + +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.GridLayoutManager +import com.shz.imagepicker.imagepicker.ImagePicker +import com.shz.imagepicker.imagepicker.ImagePickerCallback +import com.shz.imagepicker.imagepicker.model.GallerySelector +import com.shz.imagepicker.imagepicker.model.PickedResult +import com.shz.imagepicker.imagepickerapp.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity(), ImagePickerCallback { + + private val imagePicker: ImagePicker.Builder + get() = ImagePicker.Builder(this.packageName + ".provider", this) + + private lateinit var binding: ActivityMainBinding + private lateinit var adapter: ImagesDemoAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DataBindingUtil.setContentView(this, R.layout.activity_main) + + adapter = ImagesDemoAdapter() + + binding.rv.adapter = adapter + binding.rv.layoutManager = GridLayoutManager(this, 2) + + binding.btnCamera.setOnClickListener { + imagePicker + .useCamera() + .build() + .launch(this) + } + binding.btnGallery.setOnClickListener { + imagePicker + .useGallery() + .build() + .launch(this) + } + binding.btnGalCustomSingle.setOnClickListener { + imagePicker + .useGallery() + .gallerySelector(GallerySelector.CUSTOM) + .build() + .launch(this) + } + binding.btnGalCustomMultiple.setOnClickListener { + imagePicker + .useGallery() + .multipleSelection() + .gallerySelector(GallerySelector.CUSTOM) + .build() + .launch(this) + } + binding.btnDialog.setOnClickListener { + imagePicker.useGallery() + .useCamera() + .multipleSelection() + .gallerySelector(GallerySelector.CUSTOM) + .build() + .launch(this) + } + } + + override fun onImagePickerResult(result: PickedResult) { + Log.d("MainActivity", "result: $result") + when (result) { + PickedResult.Empty -> { + adapter.submitList(emptyList()) + } + is PickedResult.Error -> { + result.throwable.printStackTrace() + } + is PickedResult.Multiple -> { + adapter.submitList(result.images) + } + is PickedResult.Single -> { + adapter.submitList(listOf(result.image)) + } + } + } +} diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000..a5d172e --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_custom.xml b/app/src/main/res/drawable/ic_custom.xml new file mode 100644 index 0000000..f7ca6f7 --- /dev/null +++ b/app/src/main/res/drawable/ic_custom.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_gallery.xml b/app/src/main/res/drawable/ic_gallery.xml new file mode 100644 index 0000000..3df2119 --- /dev/null +++ b/app/src/main/res/drawable/ic_gallery.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_list.xml b/app/src/main/res/drawable/ic_list.xml new file mode 100644 index 0000000..bae7be7 --- /dev/null +++ b/app/src/main/res/drawable/ic_list.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5ed19e3..cbc1ee2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,110 +1,131 @@ - + tools:ignore="ContentDescription"> - + android:layout_height="match_parent"> - - - + - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> + - + - + - + - + - + + + - \ No newline at end of file + + + + diff --git a/app/src/main/res/layout/item_demo_image.xml b/app/src/main/res/layout/item_demo_image.xml new file mode 100644 index 0000000..0eb8b33 --- /dev/null +++ b/app/src/main/res/layout/item_demo_image.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/imagepicker/build.gradle b/imagepicker/build.gradle index 1856b0e..eb915c0 100644 --- a/imagepicker/build.gradle +++ b/imagepicker/build.gradle @@ -32,7 +32,8 @@ android { } dependencies { - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation "androidx.appcompat:appcompat:1.5.1" + implementation "androidx.recyclerview:recyclerview:1.2.1" } publishing { @@ -45,4 +46,4 @@ publishing { artifact("$buildDir/outputs/aar/${artifactId}-release.aar") } } -} \ No newline at end of file +} diff --git a/imagepicker/src/main/AndroidManifest.xml b/imagepicker/src/main/AndroidManifest.xml index 451a578..1fe0a61 100644 --- a/imagepicker/src/main/AndroidManifest.xml +++ b/imagepicker/src/main/AndroidManifest.xml @@ -9,13 +9,18 @@ + android:name=".activity.nativegallery.GalleryMultiPickerNativeActivity" + android:theme="@style/ImagePicker.Theme.Transparent" /> + android:name=".activity.nativegallery.GallerySinglePickerActivity" + android:theme="@style/ImagePicker.Theme.Transparent" /> + android:name=".activity.camera.CameraPickerActivity" + android:theme="@style/ImagePicker.Theme.Transparent" /> + + - - \ No newline at end of file + diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt deleted file mode 100644 index 4c14355..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/CameraPickerActivity.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.Manifest -import android.app.Activity -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.provider.MediaStore -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.core.content.FileProvider -import com.shz.imagepicker.imagepicker.ImagePath.getCaptureImageResultUri -import com.shz.imagepicker.imagepicker.ImagePath.getNormalizedUri -import com.shz.imagepicker.imagepicker.ImagePath.getCaptureImageOutputUri -import java.io.File - -class CameraPickerActivity : AppCompatActivity() { - - private var filename: String = "" - - private val cameraLauncher = registerForActivityResult( - ActivityResultContracts.StartActivityForResult() - ) { result -> - if (result.resultCode == Activity.RESULT_OK) { - getCaptureImageResultUri(this, result.data, filename) - ?.let { uri -> getNormalizedUri(this, uri) } - ?.path - ?.let(::File) - ?.let { file -> PickerImage(PickerSource.CAMERA, file) } - ?.let(PickerResult::Single) - ?.let(callback::onImagePickerResult) - } - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == PERMISSION_REQUEST_CAMERA) { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startCameraPicker() - } else { - finish() - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - checkCameraPermission() - } - - private fun checkCameraPermission() { - if (ContextCompat.checkSelfPermission( - this, - Manifest.permission.CAMERA - ) == PackageManager.PERMISSION_DENIED - ) { - ActivityCompat.requestPermissions( - this, arrayOf(Manifest.permission.CAMERA), - PERMISSION_REQUEST_CAMERA - ) - } else { - startCameraPicker() - } - } - - private fun startCameraPicker() { - val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) - filename = System.nanoTime().toString() - val uri = getCaptureImageOutputUri(this, filename) - if (filename.isNotEmpty()) uri?.path?.let { path -> - val file = File(path) - if (Build.VERSION.SDK_INT >= 24) { - cameraIntent.putExtra( - MediaStore.EXTRA_OUTPUT, - FileProvider.getUriForFile( - this, - authority, - file - ) - ) - cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } else { - cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri) - } - cameraLauncher.launch(cameraIntent) - } - } - - companion object { - private const val PERMISSION_REQUEST_CAMERA = 54560 - - @JvmField - internal var callback: ImagePickerCallback = ImagePickerCallback { } - internal var authority: String = "" - } -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt deleted file mode 100644 index 56cfd1e..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GalleryMultiPickerActivity.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.app.Activity -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.provider.MediaStore -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity -import com.shz.imagepicker.imagepicker.ImagePath.getImagePathFromInputStreamUri -import java.io.File - -class GalleryMultiPickerActivity : AppCompatActivity() { - - private val galleryLauncher = registerForActivityResult( - ActivityResultContracts.StartActivityForResult() - ) { result -> - if (result.resultCode == Activity.RESULT_OK) { - val output = arrayListOf() - result.data?.clipData?.let { clipData -> - for (i in 0 until clipData.itemCount) { - getImagePathFromInputStreamUri(this, clipData.getItemAt(i).uri) - ?.let(::File) - ?.takeIf(File::exists) - ?.let { file -> PickerImage(PickerSource.GALLERY_MULTIPLE, file) } - ?.let(output::add) - } - } - when (output.size) { - 0 -> null - 1 -> PickerResult.Single(output.first()) - else -> PickerResult.Multiple(output) - }?.let(callback::onImagePickerResult) - } - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == PERMISSION_CHECK_REQUEST) { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startGalleryPicker() - } else { - finish() - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - checkGalleryPermission(::startGalleryPicker) - } - - private fun startGalleryPicker() { - galleryLauncher.launch( - Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply { - if (Build.VERSION.SDK_INT >= 18) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) - setDataAndType( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - GALLERY_IMAGE_MIME - ) - } - ) - } - - companion object { - private const val GALLERY_IMAGE_MIME = "image/jpeg" - - @JvmField - internal var callback: ImagePickerCallback = ImagePickerCallback { } - } -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt deleted file mode 100644 index 2010fe9..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/GallerySinglePickerActivity.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.app.Activity -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Bundle -import android.provider.MediaStore -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity -import com.shz.imagepicker.imagepicker.ImagePath.getImagePathFromInputStreamUri -import java.io.File - -class GallerySinglePickerActivity : AppCompatActivity() { - - private val galleryLauncher = registerForActivityResult( - ActivityResultContracts.StartActivityForResult() - ) { result -> - if (result.resultCode == Activity.RESULT_OK) { - result.data - ?.data - ?.let { uri -> getImagePathFromInputStreamUri(this, uri) } - ?.let(::File) - ?.takeIf(File::exists) - ?.let { file -> PickerImage(PickerSource.GALLERY_SINGLE, file) } - ?.let(PickerResult::Single) - ?.let(callback::onImagePickerResult) - } - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == PERMISSION_CHECK_REQUEST) { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startGalleryPicker() - } else { - finish() - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - checkGalleryPermission(::startGalleryPicker) - } - - private fun startGalleryPicker() { - galleryLauncher.launch( - Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply { - setDataAndType( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - GALLERY_IMAGE_MIME - ) - } - ) - } - - companion object { - private const val GALLERY_IMAGE_MIME = "image/jpeg" - - @JvmField - internal var callback: ImagePickerCallback = ImagePickerCallback { } - } -} \ No newline at end of file diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt deleted file mode 100644 index 042724a..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePath.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Environment -import android.provider.MediaStore -import com.shz.imagepicker.imagepicker.ImagePath -import java.io.* -import java.lang.Exception -import java.util.* -import kotlin.Throws - -object ImagePath { - private const val FILE_EXTENSION_JPEG = ".jpeg" - private const val FILE_EXTENSION_JPG = ".jpg" - private const val FILE_SCHEMA_CONTENT = "content:" - - @JvmStatic - fun getCaptureImageOutputUri(context: Context?, filename: String): Uri? = context - ?.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - ?.let { file -> Uri.fromFile(File(file.path, filename + FILE_EXTENSION_JPEG)) } - - @JvmStatic - fun getCaptureImageResultUri(context: Context, data: Intent?, filename: String): Uri? { - var isCamera = true - if (data != null && data.data != null) { - val action = data.action - isCamera = action != null && action === MediaStore.ACTION_IMAGE_CAPTURE - } - return if (isCamera || data!!.data == null) getCaptureImageOutputUri(context, filename) - else data.data - } - - @JvmStatic - fun getNormalizedUri(context: Context, uri: Uri?): Uri? = - if (uri != null && uri.toString().contains(FILE_SCHEMA_CONTENT)) - Uri.fromFile( - getPath( - context, - uri, - MediaStore.Images.Media.DATA - ) - ) - else uri - - @JvmStatic - fun getImagePathFromInputStreamUri(context: Context, uri: Uri): String? { - var inputStream: InputStream? = null - var filePath: String? = null - if (uri.authority != null) { - try { - inputStream = context.contentResolver.openInputStream(uri) - val photoFile = createTempFileFrom(context, inputStream) - filePath = photoFile!!.path - } catch (ex: FileNotFoundException) { - ex.printStackTrace() - } catch (ex: Exception) { - ex.printStackTrace() - } finally { - try { - inputStream!!.close() - } catch (ex: IOException) { - ex.printStackTrace() - } - } - } - return filePath - } - - @Throws(IOException::class) - private fun createTempFileFrom(context: Context, inputStream: InputStream?): File? { - var targetFile: File? = null - if (inputStream != null) { - var read: Int - val buffer = ByteArray(8 * 1024) - targetFile = createTempFile(context, null) - val outputStream = FileOutputStream(targetFile) - while (true) { - read = inputStream.read(buffer) - if (read == -1) { - break - } - outputStream.write(buffer, 0, read) - } - outputStream.flush() - try { - outputStream.close() - } catch (ex: IOException) { - ex.printStackTrace() - } - } - return targetFile - } - - private fun createTempFile(context: Context, filePath: String?): File { - val tempFilename: String = filePath ?: Calendar.getInstance().timeInMillis.toString() - return File(context.externalCacheDir, tempFilename + FILE_EXTENSION_JPG) - } - - private fun getPath(context: Context, uri: Uri, column: String): File { - val columns = arrayOf(column) - val cursor = context.contentResolver.query(uri, columns, null, null, null) - val columnIndex = cursor!!.getColumnIndexOrThrow(column) - cursor.moveToFirst() - val path = cursor.getString(columnIndex) - cursor.close() - return File(path) - } -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt index 3cb8bc5..572259b 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePicker.kt @@ -1,55 +1,46 @@ -@file:Suppress("unused") +@file:Suppress("unused", "NOTHING_TO_INLINE") package com.shz.imagepicker.imagepicker import android.content.Context -import android.content.Intent +import android.util.Log +import com.shz.imagepicker.imagepicker.exception.NothingToLaunchException +import com.shz.imagepicker.imagepicker.model.GallerySelector +import com.shz.imagepicker.imagepicker.model.PickedResult class ImagePicker private constructor( private val authority: String, - private val callback: ImagePickerCallback = ImagePickerCallback { }, - private val useCamera: Boolean = true, - private val useGallery: Boolean = false, - private val multipleSelection: Boolean = false, - private val multipleSelectionType: MultipleSelectionType = MultipleSelectionType.NATIVE, + private val callback: ImagePickerCallback, + private val useCamera: Boolean, + private val useGallery: Boolean, + private val multipleSelection: Boolean, + private val minimumSelectionCount: Int, + private val maximumSelectionCount: Int, + private val gallerySelector: GallerySelector, ) { - fun launch(context: Context) { + fun launch(context: Context) = with(ImagePickerLauncher(context)) { when { - useCamera && !useGallery -> { - launchCameraPicker(context) - } - useGallery && !multipleSelection && !useCamera -> { - launchSingleSelectionGallery(context) - } - useGallery && multipleSelection && !useCamera -> when (multipleSelectionType) { - MultipleSelectionType.NATIVE -> launchMultipleSelectionGalleryNative(context) - MultipleSelectionType.CUSTOM -> launchMultipleSelectionGalleryCustom(context) - } - else -> Unit + useCamera && !useGallery -> launchCameraPicker(authority, callback) + !useCamera && useGallery -> launchGalleryPicker( + callback, + multipleSelection, + minimumSelectionCount, + maximumSelectionCount, + gallerySelector + ) + useCamera && useGallery -> launchDialog( + authority, + callback, + multipleSelection, + minimumSelectionCount, + maximumSelectionCount, + gallerySelector + ) + else -> deliverThrowable(callback, NothingToLaunchException()) } } - private fun launchCameraPicker(context: Context) { - CameraPickerActivity.callback = callback - CameraPickerActivity.authority = authority - context.startActivity(Intent(context, CameraPickerActivity::class.java)) - } - - private fun launchSingleSelectionGallery(context: Context) { - GallerySinglePickerActivity.callback = callback - context.startActivity(Intent(context, GallerySinglePickerActivity::class.java)) - } - - private fun launchMultipleSelectionGalleryNative(context: Context) { - GalleryMultiPickerActivity.callback = callback - context.startActivity(Intent(context, GalleryMultiPickerActivity::class.java)) - } - - private fun launchMultipleSelectionGalleryCustom(context: Context) { - //ToDo: implement me - } - class Builder( private val authority: String, private val callback: ImagePickerCallback, @@ -57,15 +48,28 @@ class ImagePicker private constructor( private var useCamera: Boolean = false private var useGallery: Boolean = false private var multipleSelection: Boolean = false - private var multipleSelectionType: MultipleSelectionType = MultipleSelectionType.NATIVE + private var minimumSelectionCount: Int = 1 + private var maximumSelectionCount: Int = Int.MAX_VALUE + private var gallerySelector: GallerySelector = GallerySelector.NATIVE - fun build(): ImagePicker = ImagePicker( - authority, - callback, - useCamera, - useGallery, - multipleSelection, - ) + fun build(): ImagePicker { + require(minimumSelectionCount >= 1) { + "Parameter minimumSelectionCount must be bigger or equal 1" + } + require(maximumSelectionCount > minimumSelectionCount) { + "Parameter maximumSelectionCount must be bigger than minimumSelectionCount" + } + return ImagePicker( + authority, + callback, + useCamera, + useGallery, + multipleSelection, + minimumSelectionCount, + maximumSelectionCount, + gallerySelector, + ) + } fun useCamera(useCamera: Boolean = true) = apply { this.useCamera = useCamera @@ -75,12 +79,20 @@ class ImagePicker private constructor( this.useGallery = useGallery } - fun multipleSelection(multipleSelection: Boolean) = apply { + fun multipleSelection(multipleSelection: Boolean = true) = apply { this.multipleSelection = multipleSelection } - fun multipleSelectionType(multipleSelectionType: MultipleSelectionType) = apply { - this.multipleSelectionType = multipleSelectionType + fun minimumSelectionCount(minimumSelectionCount: Int) = apply { + this.minimumSelectionCount = minimumSelectionCount + } + + fun maximumSelectionCount(maximumSelectionCount: Int) = apply { + this.maximumSelectionCount = maximumSelectionCount + } + + fun gallerySelector(gallerySelector: GallerySelector) = apply { + this.gallerySelector = gallerySelector } companion object { @@ -94,4 +106,26 @@ class ImagePicker private constructor( .build() } } + + companion object { + private const val TAG = "ShzImagePicker" + + internal inline fun deliverThrowable(clb: ImagePickerCallback, t: Throwable) { + t.also(::errorLog) + .let(PickedResult::Error) + .let(clb::onImagePickerResult) + } + + internal inline fun errorLog(t: Throwable) { + if (BuildConfig.DEBUG) Log.e(TAG, t.message.toString(), t) + } + + internal inline fun errorLog(message: String, t: Throwable? = null) { + if (BuildConfig.DEBUG) Log.e(TAG, message, t) + } + + internal inline fun debugLog(message: String) { + if (BuildConfig.DEBUG) Log.d(TAG, message) + } + } } diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt index a2a3506..c8b057c 100644 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerCallback.kt @@ -1,5 +1,7 @@ package com.shz.imagepicker.imagepicker +import com.shz.imagepicker.imagepicker.model.PickedResult + fun interface ImagePickerCallback { - fun onImagePickerResult(result: PickerResult) + fun onImagePickerResult(result: PickedResult) } diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt deleted file mode 100644 index 3a17735..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerDialog.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.app.Dialog -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.Window -import android.widget.LinearLayout -import androidx.fragment.app.DialogFragment - -class ImagePickerDialog(private val listener: ImagePickerDialogListener) : DialogFragment() { - - interface ImagePickerDialogListener { - fun onCamera() - fun onGallery() - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) - dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) - return dialog - } - - override fun onStart() { - super.onStart() - val dialog = dialog - if (dialog != null) { - //dialog.getWindow().setLayout(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - val params = dialog.window!!.attributes - //params.horizontalMargin = 0.01f; - dialog.window!!.attributes = params - dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.WHITE)) - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val rootView = inflater.inflate(R.layout.layout_image_picker, container, false) - (rootView.findViewById(R.id.btn_camera) as LinearLayout).setOnClickListener { - listener.onCamera() - dismissAllowingStateLoss() - } - (rootView.findViewById(R.id.btn_gallery) as LinearLayout).setOnClickListener { - listener.onGallery() - dismissAllowingStateLoss() - } - return rootView - } -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerLauncher.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerLauncher.kt new file mode 100644 index 0000000..b445df7 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/ImagePickerLauncher.kt @@ -0,0 +1,95 @@ +package com.shz.imagepicker.imagepicker + +import android.content.Context +import android.content.Intent +import com.shz.imagepicker.imagepicker.activity.camera.CameraPickerActivity +import com.shz.imagepicker.imagepicker.activity.customgallery.GalleryPickerCustomActivity +import com.shz.imagepicker.imagepicker.activity.dialog.DialogLauncherActivity +import com.shz.imagepicker.imagepicker.activity.nativegallery.GalleryMultiPickerNativeActivity +import com.shz.imagepicker.imagepicker.activity.nativegallery.GallerySinglePickerActivity +import com.shz.imagepicker.imagepicker.model.GallerySelector +import kotlin.math.max + +internal class ImagePickerLauncher(private val context: Context) { + + fun launchCameraPicker( + authority: String, + callback: ImagePickerCallback, + ) { + ImagePicker.debugLog("[Launcher] launchCameraPicker") + CameraPickerActivity.authority = authority + CameraPickerActivity.callback = callback + context.startActivity(Intent(context, CameraPickerActivity::class.java)) + } + + fun launchGalleryPicker( + callback: ImagePickerCallback, + multipleSelection: Boolean, + minimum: Int, + maximum: Int, + gallerySelector: GallerySelector, + ) { + ImagePicker.debugLog("[Launcher] launchGalleryPicker") + when (gallerySelector) { + GallerySelector.NATIVE -> if (multipleSelection) { + launchMultipleSelectionGalleryNative(callback) + } else { + launchSingleSelectionGallery(callback) + } + GallerySelector.CUSTOM -> { + launchMultipleSelectionGalleryCustom(callback, multipleSelection, minimum, maximum) + } + } + } + + fun launchDialog( + authority: String, + callback: ImagePickerCallback, + multipleSelection: Boolean, + minimum: Int, + maximum: Int, + gallerySelector: GallerySelector, + ) { + ImagePicker.debugLog("[Launcher] launchDialog") + DialogLauncherActivity.callback = callback + context.startActivity(Intent(context, DialogLauncherActivity::class.java).apply { + putExtra( + DialogLauncherActivity.BUNDLE_PAYLOAD, + DialogLauncherActivity.Payload( + authority, + multipleSelection, + minimum, + maximum, + gallerySelector, + ) + ) + }) + } + + private fun launchSingleSelectionGallery(callback: ImagePickerCallback) { + ImagePicker.debugLog("[Launcher] launchSingleSelectionGallery") + GallerySinglePickerActivity.callback = callback + context.startActivity(Intent(context, GallerySinglePickerActivity::class.java)) + } + + private fun launchMultipleSelectionGalleryNative(callback: ImagePickerCallback) { + ImagePicker.debugLog("[Launcher] launchMultipleSelectionGalleryNative") + GalleryMultiPickerNativeActivity.callback = callback + context.startActivity(Intent(context, GalleryMultiPickerNativeActivity::class.java)) + } + + private fun launchMultipleSelectionGalleryCustom( + callback: ImagePickerCallback, + multipleSelection: Boolean, + min: Int, + max: Int, + ) { + ImagePicker.debugLog("[Launcher] launchMultipleSelectionGalleryCustom") + GalleryPickerCustomActivity.callback = callback + context.startActivity(Intent(context, GalleryPickerCustomActivity::class.java).apply { + putExtra(GalleryPickerCustomActivity.BUNDLE_MULTI_SELECTION, multipleSelection) + putExtra(GalleryPickerCustomActivity.BUNDLE_MINIMUM, min) + putExtra(GalleryPickerCustomActivity.BUNDLE_MAXIMUM, max) + }) + } +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt deleted file mode 100644 index 1941407..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/MultipleSelectionType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.shz.imagepicker.imagepicker - -enum class MultipleSelectionType { - NATIVE, - CUSTOM; -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt deleted file mode 100644 index dbb818b..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PermissionChecker.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.shz.imagepicker.imagepicker - -import android.Manifest -import android.app.Activity -import android.content.pm.PackageManager -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat - -const val PERMISSION_CHECK_REQUEST = 54567 - -fun Activity.checkGalleryPermission(action: () -> Unit) { - val hasNoReadPermission = ContextCompat.checkSelfPermission( - this, - Manifest.permission.READ_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_DENIED - - val hasNoWritePermission = ContextCompat.checkSelfPermission( - this, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_DENIED - - if (hasNoReadPermission || hasNoWritePermission) - ActivityCompat.requestPermissions( - this, - arrayOf( - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - ), - PERMISSION_CHECK_REQUEST, - ) - else action() -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt deleted file mode 100644 index 5c33127..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.shz.imagepicker.imagepicker - -sealed class PickerResult { - data class Single(val image: PickerImage) : PickerResult() - data class Multiple(val images: List) : PickerResult() -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt deleted file mode 100644 index 4aae1a4..0000000 --- a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/PickerSource.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.shz.imagepicker.imagepicker - -enum class PickerSource { - GALLERY_SINGLE, - GALLERY_MULTIPLE, - CAMERA; -} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/camera/CameraPickerActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/camera/CameraPickerActivity.kt new file mode 100644 index 0000000..1a1ace7 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/camera/CameraPickerActivity.kt @@ -0,0 +1,70 @@ +package com.shz.imagepicker.imagepicker.activity.camera + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import androidx.core.content.FileProvider +import com.shz.imagepicker.imagepicker.ImagePickerCallback +import com.shz.imagepicker.imagepicker.core.ImagePickerActivity +import com.shz.imagepicker.imagepicker.model.PickedImage +import com.shz.imagepicker.imagepicker.model.PickedResult +import com.shz.imagepicker.imagepicker.model.PickedSource +import com.shz.imagepicker.imagepicker.utils.checkCameraPermission +import com.shz.imagepicker.imagepicker.utils.getCaptureImageOutputUri +import com.shz.imagepicker.imagepicker.utils.getCaptureImageResultUri +import com.shz.imagepicker.imagepicker.utils.getNormalizedUri +import java.io.File + +internal class CameraPickerActivity : ImagePickerActivity() { + + override val requestCode: Int = 54500 + + private var filename: String = "" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkCameraPermission(requestCode, ::startPicker) + } + + override fun deliverResult(intent: Intent?) { + super.deliverResult(intent) + getCaptureImageResultUri(this, intent, filename) + ?.let { uri -> getNormalizedUri(this, uri) } + ?.path + ?.let(::File) + ?.let { file -> PickedImage(PickedSource.CAMERA, file) } + ?.let(PickedResult::Single) + ?.let(callback::onImagePickerResult) + } + + override fun startPicker() { + val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + filename = System.nanoTime().toString() + val uri = getCaptureImageOutputUri(this, filename) + if (filename.isNotEmpty()) uri?.path?.let { path -> + val file = File(path) + if (Build.VERSION.SDK_INT >= 24) { + cameraIntent.putExtra( + MediaStore.EXTRA_OUTPUT, + FileProvider.getUriForFile( + this, + authority, + file, + ) + ) + cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } else { + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri) + } + launcher.launch(cameraIntent) + } + } + + companion object { + @JvmField + internal var callback: ImagePickerCallback = ImagePickerCallback { } + internal var authority: String = "" + } +} diff --git a/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/GalleryPickerCustomActivity.kt b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/GalleryPickerCustomActivity.kt new file mode 100644 index 0000000..9af29f4 --- /dev/null +++ b/imagepicker/src/main/java/com/shz/imagepicker/imagepicker/activity/customgallery/GalleryPickerCustomActivity.kt @@ -0,0 +1,110 @@ +package com.shz.imagepicker.imagepicker.activity.customgallery + +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import android.widget.Button +import android.widget.FrameLayout +import android.widget.TextView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.shz.imagepicker.imagepicker.ImagePicker +import com.shz.imagepicker.imagepicker.ImagePickerCallback +import com.shz.imagepicker.imagepicker.R +import com.shz.imagepicker.imagepicker.activity.customgallery.adapter.multiple.GalleryImagesMultipleAdapter +import com.shz.imagepicker.imagepicker.activity.customgallery.adapter.single.GalleryImagesSingleAdapter +import com.shz.imagepicker.imagepicker.core.ImagePickerActivity +import com.shz.imagepicker.imagepicker.model.PickedImage +import com.shz.imagepicker.imagepicker.model.PickedResult +import com.shz.imagepicker.imagepicker.model.PickedSource +import com.shz.imagepicker.imagepicker.utils.checkGalleryPermission +import com.shz.imagepicker.imagepicker.utils.getAllImages +import java.io.File + +internal class GalleryPickerCustomActivity : ImagePickerActivity() { + + override val requestCode: Int = 54503 + + private var multipleSelection = false + private var max = Int.MAX_VALUE + private var min = 1 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.image_picker_activity_gallery_custom) + + multipleSelection = intent.getBooleanExtra(BUNDLE_MULTI_SELECTION, false) + max = intent.getIntExtra(BUNDLE_MAXIMUM, 10) + min = intent.getIntExtra(BUNDLE_MINIMUM, 1) + + findViewById(R.id.bg_bottom_view)?.isVisible = multipleSelection + findViewById(R.id.fl_bottom_view)?.isVisible = multipleSelection + + findViewById