Skip to content

Instantly share code, notes, and snippets.

@amay077
Forked from koral--/ImageCaptureHelper.java
Last active January 16, 2019 11:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amay077/bb30ba8681ef8916e3efb6edc86701c8 to your computer and use it in GitHub Desktop.
Save amay077/bb30ba8681ef8916e3efb6edc86701c8 to your computer and use it in GitHub Desktop.
Helper for sending ACTION_IMAGE_CAPTURE intent and retrieve its results. Handles all low level operations
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.awesome.app">
<application>
<!-- need provider tag in application tag -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<!-- ressource file to create -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<!-- locate res/xml/file_paths.xml -->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>
package your.awesome.app
//MIT License
//Copyright (c) 2015 Karol Wrótniak, Droids On Roids
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import android.provider.MediaStore.Images.ImageColumns
import android.provider.MediaStore.MediaColumns
import android.support.v4.content.FileProvider
import net.nepula.nippan_android.BuildConfig
import java.io.File
import java.util.regex.Pattern
/**
* Helper for sending ACTION_IMAGE_CAPTURE intent and retrieve its results. Handles all low level operations
* <br></br>
* Usage:<br></br>
*
* 1. Launch camera app by calling [.launchCameraApp], save resulting Uri, handle (or ignore) exceptions.
* 1. In your onActivityResult(int requestCode, int resultCode, Intent data) call [) ][.retrievePhotoResult]. You may want to inform user if photo cannot be read despite the result is RESULT_OK.
*
* @author koral--
*/
object ImageCaptureHelper {
private val PROJECTION = arrayOf(MediaColumns.DATA)
private val CONTENT_VALUES = ContentValues(1)
private val PHOTO_SHARED_PREFS_NAME = "photo_shared"
private val PHOTO_URI = "photo_uri"
/**
* Description text inserted into @link{ImageColumns.DESCRIPTION} column
*/
val DESCRIPTION = "Photo taken with example application"
init {
CONTENT_VALUES.put(ImageColumns.DESCRIPTION, DESCRIPTION)
}
/**
* Tries to obtain File containing taken photo. Perform cleanups if photo was not taken or it is empty.
* @param caller caller Activity
* @param photoKey key in Shared Preferences for taken image, can be null
* @return File containing photo or null if no or empty photo was saved by camera app.
*/
fun retrievePhotoResult(caller: Activity, photoKey: String?): File? {
var photoKey = photoKey
try {
if (photoKey == null) {
photoKey = PHOTO_URI
}
val prefs = caller.getSharedPreferences(PHOTO_SHARED_PREFS_NAME, Context.MODE_PRIVATE)
val takenPhotoUriString = prefs.getString(photoKey, null)
prefs.edit().remove(photoKey).commit()
if (takenPhotoUriString == null) {
return null
}
val takenPhotoUri = Uri.parse(takenPhotoUriString)
val cr = caller.contentResolver
val out = File(getPhotoFilePath(takenPhotoUri, cr)!!)
if (!out.isFile || out.length() == 0L) {
cr.delete(takenPhotoUri, null, null)
} else {
return out
}
} catch (ex: Exception) {
// no-op
}
return null
}
/**
* Tries to create photo placeholder and launch camera app
* @param requestCode your unique code that will be returned in onActivityResult
* @param caller caller Activity
* @param photoKey key in Shared Preferences for taken image, can be null
* @throws ActivityNotFoundException if no camera app was found
* @throws Exception if there is a problem with create photo eg. SDcard is not mounted. It may be eg. [IllegalStateException] or [UnsupportedOperationException] depending on [ContentResolver].
*/
@Throws(ActivityNotFoundException::class, Exception::class)
fun launchCameraApp(requestCode: Int, caller: Activity, photoKey: String?) {
var photoKey = photoKey
if (photoKey == null) {
photoKey = PHOTO_URI
}
val cr = caller.contentResolver
val takenPhotoUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, CONTENT_VALUES)
?: throw IllegalStateException("Photo insertion failed")
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, getIntentUri(caller, takenPhotoUri, cr))
caller.startActivityForResult(intent, requestCode)
val prefs = caller.getSharedPreferences(PHOTO_SHARED_PREFS_NAME, Context.MODE_PRIVATE)
prefs.edit().putString(photoKey, takenPhotoUri.toString()).commit()
}
private fun getIntentUri(context: Context, takenPhotoUri: Uri, cr: ContentResolver): Uri {
val path = getPhotoFilePath(takenPhotoUri, cr)
?: throw IllegalStateException("Photo resolution failed")
return FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.provider", //(use your app signature + ".provider" )
File(path));
// return Uri.fromFile(File(path))
}
private fun getPhotoFilePath(takenPhotoUri: Uri, cr: ContentResolver): String? {
val cursor = cr.query(takenPhotoUri, PROJECTION, null, null, null)
var res: String? = null
if (cursor != null) {
val dataIdx = cursor.getColumnIndex(MediaColumns.DATA)
if (dataIdx >= 0 && cursor.moveToFirst())
res = cursor.getString(dataIdx)
cursor.close()
}
return res
}
/**
* Dirty hack for API level <8 to get a top-level public external storage directory where Camera photos should be placed.<br></br>
* Empty photo is inserted, path of its parent directory is retrieved and then photo is deleted.<br></br>
* If photo cannot be inserted eg. external storage is not mounted, then "DCIM" folder in root of the external storage is used as a fallback.
* @param cr [ContentResolver] used to resolve image Uris
* @return path to directory where camera app places photos (may be fallback)
*/
fun getPhotoDirPath(cr: ContentResolver): String {
val fallback = Environment.getExternalStorageDirectory().toString() + "/DCIM"
var takenPhotoUri: Uri? = null
var photoFilePath: String? = null
try {
takenPhotoUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, CONTENT_VALUES)
if (takenPhotoUri == null)
return fallback
photoFilePath = getPhotoFilePath(takenPhotoUri, cr)
cr.delete(takenPhotoUri, null, null)
} catch (ex: Exception) {
//igonred
}
if (photoFilePath == null)
return fallback
var parent = File(photoFilePath).parent
val m = Pattern.compile("/DCIM(/|$)").matcher(parent)
if (m.find())
parent = parent.substring(0, m.end())
return parent
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment