Git Product home page Git Product logo

choosephotohelper's Introduction

ChoosePhotoHelper

Android Arsenal Download Codacy Badge

Picking an image as an avatar in Android apps needs to write a bunch of error-prone boilerplate codes. ChoosePhotoHelper develops a component which facilitates picking photos from gallery or taking an image with the camera without any boilerplate codes. It also internally handles some issues like rotation correction of the taken photos, permission request for camera and gallery (if needed), URI exposure problem, etc.

Take a Photo Choose from Gallery

Download

ChoosePhotoHelper is available on bintray to download using build tools systems. Add the following lines to your build.gradle file:

repositories {
    jcenter()
}

dependencies {
    implementation 'com.aminography:choosephotohelper:1.3.1'
}

Usage

First of all, ChoosePhotoHelper provides 3 types of result to access the chosen photo which are:

Builder Method Result Type
asFilePath() String
asUri() Uri
asBitmap() Bitmap

Now, use it simply in 3 steps:

• First

Create an instance of ChoosePhotoHelper using its builder pattern specifying the result type:

choosePhotoHelper = ChoosePhotoHelper.with(activity)
        .asFilePath()
        .build(new ChoosePhotoCallback<String>() {
            @Override
            public void onChoose(String photo) {
                Glide.with(imageView)
                        .load(photo)
                        .into(imageView);
            }
        });

• Second

Override onActivityResult and onRequestPermissionsResult in your Activity / Fragment class, then forward their result to the choosePhotoHelper instance:

@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    choosePhotoHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    choosePhotoHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

• Finally

Call showChooser() method on the choosePhotoHelper instance:

choosePhotoHelper.showChooser();

Here is a detailed example which is written in kotlin:

class MainActivity : AppCompatActivity() {

    private lateinit var choosePhotoHelper: ChoosePhotoHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        choosePhotoHelper = ChoosePhotoHelper.with(this)
            .asFilePath()
            .withState(savedInstanceState)
            .build {
                Glide.with(this)
                    .load(it)
                    .into(imageView)
            }

        button.setOnClickListener {
            choosePhotoHelper.showChooser()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        choosePhotoHelper.onActivityResult(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        choosePhotoHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        choosePhotoHelper.onSaveInstanceState(outState)
    }

}

Change Log

Version 1.3.0

  • Migrating to Kotlin 1.4.0.

Version 1.2.1

  • Some minor improvements.
  • Adding ability to always show remove photo option.

Version 1.2.0

  • File path problem targeting api 29 is fixed.

Version 1.1.0

  • Migrating to AndroidX.
  • Adding onSaveInstanceState to save and restore state.

License

Copyright 2019 Mohammad Amin Hassani.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

choosephotohelper's People

Contributors

aminography avatar kerleba avatar vogster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

choosephotohelper's Issues

Build for bitmap and URI response not working

I used it but I had to rewrite the ChoosePhotoHelper because the builders for URI and bitmap responses don't work. I made this change:
`
class ChoosePhotoHelper private constructor(
private val activity: Activity,
private val callback: ChoosePhotoCallback<*>,
private var outputType: OutputType
) {

private var filePath: String? = null
private var cameraFilePath: String? = null

fun showChooser() {
    AlertDialog.Builder(activity).apply {
        setTitle(R.string.choose_photo_using)
        setNegativeButton(R.string.action_close, null)

        val items: List<Map<String, Any>> = if (!filePath.isNullOrBlank()) {
            mutableListOf<Map<String, Any>>(
                mutableMapOf(
                    "title" to activity.getString(R.string.camera),
                    "icon" to R.drawable.ic_photo_camera_black_24dp
                ),
                mutableMapOf(
                    "title" to activity.getString(R.string.gallery),
                    "icon" to R.drawable.ic_photo_black_24dp
                ),
                mutableMapOf(
                    "title" to activity.getString(R.string.remove_photo),
                    "icon" to R.drawable.ic_delete_black_24dp
                )
            )
        } else {
            mutableListOf<Map<String, Any>>(
                mutableMapOf(
                    "title" to activity.getString(R.string.camera),
                    "icon" to R.drawable.ic_photo_camera_black_24dp
                ),
                mutableMapOf(
                    "title" to activity.getString(R.string.gallery),
                    "icon" to R.drawable.ic_photo_black_24dp
                )
            )
        }
        val adapter = SimpleAdapter(
            activity,
            items,
            R.layout.simple_list_item,
            arrayOf("title", "icon"),
            intArrayOf(R.id.textView, R.id.imageView)
        )
        setAdapter(adapter) { _, which ->
            when (which) {
                0 -> checkAndStartCamera()
                1 -> checkAndShowPicker()
                2 -> {
                    filePath = null
                    callback.onChoose(null)
                }
            }
        }
        val dialog = create()
        dialog.listView.setPadding(0, activity.dip(16), 0, 0)
        dialog.show()
    }
}

fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            REQUEST_CODE_TAKE_PHOTO -> {
                filePath = cameraFilePath
            }
            REQUEST_CODE_PICK_PHOTO -> {
                filePath = pathFromUri(
                    activity,
                    Uri.parse(intent?.data?.toString())
                )
            }
        }
        filePath?.apply {
            @Suppress("UNCHECKED_CAST")
            when (outputType) {
                OutputType.FILE_PATH -> {
                    (callback as ChoosePhotoCallback<String>).onChoose(filePath)
                }
                OutputType.URI -> {
                    val uri = Uri.fromFile(File(filePath))
                    (callback as ChoosePhotoCallback<Uri>).onChoose(uri)
                }
                OutputType.BITMAP -> {
                    doAsync {
                        //                            val bitmapBytes = modifyOrientationAndResize(this@apply)
                        var bitmap = BitmapFactory.decodeFile(this@apply)
                        try {
                            bitmap = modifyOrientation(
                                bitmap,
                                this@apply
                            )
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                        uiThread {
                            (callback as ChoosePhotoCallback<Bitmap>).onChoose(bitmap)
                        }
                    }
                }
            }
        }
    }
}

fun onRequestPermissionsResult(
    requestCode: Int,
    @Suppress("UNUSED_PARAMETER") permissions: Array<String>,
    grantResults: IntArray
) {
    when (requestCode) {
        REQUEST_CODE_TAKE_PHOTO_PERMISSION -> {
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED)) {
                onPermissionsGranted(requestCode)
            } else {
                activity.toast(R.string.required_permissions_are_not_granted)
            }
        }
        REQUEST_CODE_PICK_PHOTO_PERMISSION -> {
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                onPermissionsGranted(requestCode)
            } else {
                activity.toast(R.string.required_permission_is_not_granted)
            }
        }
    }
}

private fun onPermissionsGranted(requestCode: Int) {
    when (requestCode) {
        REQUEST_CODE_TAKE_PHOTO_PERMISSION -> {
            val picturesPath =
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
            val takePicture = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            cameraFilePath =
                picturesPath.toString() +
                        File.separator +
                        SimpleDateFormat(
                            "yyyy-MMM-dd_HH-mm-ss",
                            Locale.getDefault()
                        ).format(Date()) +
                        ".jpg"
            takePicture.putExtra(
                MediaStore.EXTRA_OUTPUT,
                uriFromFile(
                    activity,
                    activity.application.packageName,
                    File(cameraFilePath)
                )
            )
            takePicture.putExtra(MediaStore.EXTRA_SIZE_LIMIT, CAMERA_MAX_FILE_SIZE_BYTE)
            activity.startActivityForResult(
                takePicture,
                REQUEST_CODE_TAKE_PHOTO
            )
        }
        REQUEST_CODE_PICK_PHOTO_PERMISSION -> {
            val intent = Intent()
            intent.type = "image/*"
            intent.action = Intent.ACTION_GET_CONTENT
            intent.addCategory(Intent.CATEGORY_OPENABLE)
            activity.startActivityForResult(
                Intent.createChooser(intent, "Choose Photo"),
                REQUEST_CODE_PICK_PHOTO
            )
        }
    }
}

private fun checkAndStartCamera() {
    if (hasPermissions(
            activity,
            *TAKE_PHOTO_PERMISSIONS
        )
    ) {
        onPermissionsGranted(
            REQUEST_CODE_TAKE_PHOTO_PERMISSION
        )
    } else {
        ActivityCompat.requestPermissions(
            activity,
            TAKE_PHOTO_PERMISSIONS,
            REQUEST_CODE_TAKE_PHOTO_PERMISSION
        )
    }
}

private fun checkAndShowPicker() {
    if (hasPermissions(
            activity,
            *PICK_PHOTO_PERMISSIONS
        )
    ) {
        onPermissionsGranted(
            REQUEST_CODE_PICK_PHOTO_PERMISSION
        )
    } else {
        ActivityCompat.requestPermissions(
            activity,
            PICK_PHOTO_PERMISSIONS,
            REQUEST_CODE_PICK_PHOTO_PERMISSION
        )
    }
}

enum class OutputType {
    FILE_PATH,
    URI,
    BITMAP
}

abstract class BaseRequestBuilder<T> internal constructor(
    private val activity: Activity,
    val outputType: OutputType
) {

    fun build(callback: ChoosePhotoCallback<T>): ChoosePhotoHelper {
        return ChoosePhotoHelper(activity, callback, outputType)
    }
}

class FilePathRequestBuilder internal constructor(activity: Activity) :
    BaseRequestBuilder<String>(activity, OutputType.FILE_PATH)

class UriRequestBuilder internal constructor(activity: Activity) :
    BaseRequestBuilder<Uri>(activity, OutputType.URI)

class BitmapRequestBuilder internal constructor(activity: Activity) :
    BaseRequestBuilder<Bitmap>(activity, OutputType.BITMAP)

class RequestBuilder(private val activity: Activity) {

    fun asFilePath(): FilePathRequestBuilder {
        return FilePathRequestBuilder(activity)
    }

    fun asUri(): UriRequestBuilder {
        return UriRequestBuilder(activity)
    }

    fun asBitmap(): BitmapRequestBuilder {
        return BitmapRequestBuilder(activity)
    }
}

companion object {
    private const val CAMERA_MAX_FILE_SIZE_BYTE = 2 * 1024 * 1024
    private const val REQUEST_CODE_TAKE_PHOTO = 101
    private const val REQUEST_CODE_PICK_PHOTO = 102

    val TAKE_PHOTO_PERMISSIONS =
        arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
    const val REQUEST_CODE_TAKE_PHOTO_PERMISSION = 103

    val PICK_PHOTO_PERMISSIONS = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    const val REQUEST_CODE_PICK_PHOTO_PERMISSION = 104

    @JvmStatic
    fun with(activity: Activity): RequestBuilder = RequestBuilder(activity)
}

}
`
I hope you find it useful.

update documentation

Hello, can you update your documentation with this :

If you target Android 10 or higher(targetSdkVersion >= 29), set the value of requestLegacyExternalStorage to true in your app's manifest file:

<manifest ... >

<application android:requestLegacyExternalStorage="true" ... >
...

doesn't work with AndroidX

Hi! Thanks for the library, that's exactly what I needed. But it doesn't work with my project because I use androidX.
Would you consider migrating to androidX?

Improvement: Option to provide request code

What if in one activity of fragment we need to choose multiple type of photos like different Id (for e.g passport, national card, driving licence etc.) ), this can be achieved by passing request from calling function, but currently request code is hard-coded in lib it self.

Callback is not called if activity was restarted

If caller activity gets restarted during taking photo, cameraFilePath is null when onActivityResult is called.

Steps to reproduce:

  1. Turn on Don't keep activities setting.

  2. Take photo

  3. Callback gets never called because cameraFilePath is null.

Thanks for your work on library.

Cropper

please set a cropper after choose photo

Option to chose delete button

Add an option to use the delete button even if no image is chosen.
Using this in projects like firebase the image is loaded but option to show delete is not shown
#14

Crash if one permission granted and then grant second permission when try to launch camera

There is next code in onRequestPermissionsResult() for camera:

if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED)) {
onPermissionsGranted(requestCode)
}

But in grantResults[] will be only one object and java.lang.ArrayIndexOutOfBoundsException: length=1; index=1 will be thrown.

Simple fix is check is grantResults[] has only one object.

Take a photo don't work on api 29

I choose the version 1.2.0 but only 1 mode work.

When i "choose from gallery" it work but not "take a photo" since i upgraded with targetSdkVersion 29 and also with the 1.2.0.
The data from the intent onActivityResult is null.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.