aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-08-18 14:39:40 -0700
committerAllan Wang <me@allanwang.ca>2017-08-30 12:24:19 -0400
commita101b528efdee74fc1970b7f1fe68263f0b20269 (patch)
tree8fbd0fea544ec543cdf5ea2b74d81099d4ebd554
parent9432652b03ae4d01e3dda4325984637d9523b9e2 (diff)
downloadkau-a101b528efdee74fc1970b7f1fe68263f0b20269.tar.gz
kau-a101b528efdee74fc1970b7f1fe68263f0b20269.tar.bz2
kau-a101b528efdee74fc1970b7f1fe68263f0b20269.zip
Create media picker action items (#40)
* Create action items * Increment version * Update camera action * Abstract camera action * Add and test * Refactor and add docs
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt18
-rw-r--r--gradle.properties2
-rw-r--r--mediapicker/README.md21
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt154
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaItemBasic.kt7
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaModel.kt8
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt12
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityOverlayBase.kt5
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt109
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaType.kt16
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaUtils.kt59
-rw-r--r--mediapicker/src/main/res/values/ids.xml4
-rw-r--r--mediapicker/src/main/res/values/strings.xml7
-rw-r--r--sample/src/main/AndroidManifest.xml11
-rw-r--r--sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt4
-rw-r--r--sample/src/main/kotlin/ca/allanwang/kau/sample/MediaPicker.kt26
-rw-r--r--sample/src/main/res/xml/kau_file_paths.xml9
17 files changed, 423 insertions, 49 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt
index 25e0519..b03707d 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt
@@ -11,23 +11,5 @@ import java.util.*
/**
* Created by Allan Wang on 2017-08-04.
*/
-@Throws(IOException::class)
-fun createMediaFile(prefix: String, extension: String): File {
- val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
- val imageFileName = "${prefix}_${timeStamp}_"
- val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
- val frostDir = File(storageDir, prefix)
- if (!frostDir.exists()) frostDir.mkdirs()
- return File.createTempFile(imageFileName, extension, frostDir)
-}
-
-@Throws(IOException::class)
-fun Context.createPrivateMediaFile(prefix: String, extension: String): File {
- val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
- val imageFileName = "${prefix}_${timeStamp}_"
- val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
- return File.createTempFile(imageFileName, extension, storageDir)
-}
-
fun File.copyFromInputStream(inputStream: InputStream)
= inputStream.use { input -> outputStream().use { output -> input.copyTo(output) } } \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index b738642..d7837c8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -23,7 +23,7 @@ TARGET_SDK=26
BUILD_TOOLS=26.0.1
ANDROID_SUPPORT_LIBS=26.0.0
-VERSION_NAME=3.3.0
+VERSION_NAME=3.3.3
KOTLIN=1.1.3-2
ABOUT_LIBRARIES=5.9.7
diff --git a/mediapicker/README.md b/mediapicker/README.md
index a743a47..0eb6fd5 100644
--- a/mediapicker/README.md
+++ b/mediapicker/README.md
@@ -40,4 +40,23 @@ Note that this launches the activity through a `startActivityForResult` call
You may get the activity response by overriding your `onActivityResult` method
to first verify that the request code matches and then call `kauOnMediaPickerResult`,
-which will return the list of MediaModels. \ No newline at end of file
+which will return the list of MediaModels.
+
+## MediaActions
+
+On top of retrieving your media file, you may also add action items to the start
+of the grid. All actions will return their results immediately, and retrieve media types based on the activity.
+
+### MediaActionCamera
+
+Gets an image or a video from the default camera. No permissions are necessary.
+Note that since api 24, passing general uris may throw a [FileUriExposedException](https://developer.android.com/reference/android/os/FileUriExposedException.html),
+so your own resolvers need to be passed for this to work. See the sample xml folder for an example.
+
+### MediaActionCameraVideo
+
+Given that getting videos do not require resolvers, this item can be used for videos only without any required arguments.
+
+### MediaActionGallery
+
+Defines whether you want to pick one or more media items from the default gallery app. \ No newline at end of file
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt
new file mode 100644
index 0000000..5910650
--- /dev/null
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt
@@ -0,0 +1,154 @@
+package ca.allanwang.kau.mediapicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.MediaStore
+import ca.allanwang.kau.iitems.KauIItem
+import ca.allanwang.kau.permissions.PERMISSION_READ_EXTERNAL_STORAGE
+import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
+import ca.allanwang.kau.permissions.kauRequestPermissions
+import ca.allanwang.kau.utils.materialDialog
+import ca.allanwang.kau.utils.string
+import com.mikepenz.google_material_typeface_library.GoogleMaterial
+import com.mikepenz.iconics.typeface.IIcon
+import java.io.File
+
+
+/**
+ * Created by Allan Wang on 2017-08-17.
+ */
+class MediaActionItem(
+ val action: MediaAction,
+ val mediaType: MediaType
+) : KauIItem<MediaActionItem, MediaItemBasic.ViewHolder>(R.layout.kau_iitem_image_basic, { MediaItemBasic.ViewHolder(it) }, R.id.kau_item_media_action) {
+
+ override fun isSelectable(): Boolean = false
+
+ override fun bindView(holder: MediaItemBasic.ViewHolder, payloads: MutableList<Any>?) {
+ super.bindView(holder, payloads)
+ holder.image.apply {
+ setImageDrawable(MediaPickerCore.getIconDrawable(context, action.iicon(this@MediaActionItem), action.color))
+ setOnClickListener { action.invoke(context, this@MediaActionItem) }
+ }
+ }
+
+ override fun unbindView(holder: MediaItemBasic.ViewHolder) {
+ super.unbindView(holder)
+ holder.image.apply {
+ setImageDrawable(null)
+ setOnClickListener(null)
+ }
+ }
+}
+
+interface MediaAction {
+ var color: Int
+ fun iicon(item: MediaActionItem): IIcon
+ fun invoke(c: Context, item: MediaActionItem)
+}
+
+internal const val MEDIA_ACTION_REQUEST_CAMERA = 100
+internal const val MEDIA_ACTION_REQUEST_PICKER = 101
+
+/**
+ * Dynamic camera items for both images and videos
+ * Given that images require a uri to save the file, they must be implemented on top
+ * of this abstract class.
+ *
+ * If you just wish to use videos, see [MediaActionCameraVideo]
+ */
+abstract class MediaActionCamera(
+ override var color: Int = MediaPickerCore.accentColor
+) : MediaAction {
+
+ abstract fun createFile(context: Context): File
+ abstract fun createUri(context: Context, file: File): Uri
+
+ override fun iicon(item: MediaActionItem) = when (item.mediaType) {
+ MediaType.IMAGE -> GoogleMaterial.Icon.gmd_photo_camera
+ MediaType.VIDEO -> GoogleMaterial.Icon.gmd_videocam
+ }
+
+ override fun invoke(c: Context, item: MediaActionItem) {
+ c.kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) {
+ granted, _ ->
+ if (granted) {
+ val intent = Intent(item.mediaType.captureType)
+ if (intent.resolveActivity(c.packageManager) == null) {
+ c.materialDialog {
+ title(R.string.kau_no_camera_found)
+ content(R.string.kau_no_camera_found_content)
+ }
+ return@kauRequestPermissions
+ }
+ if (item.mediaType == MediaType.IMAGE) {
+ val file: File = try {
+ createFile(c)
+ } catch (e: java.io.IOException) {
+ c.materialDialog {
+ title(R.string.kau_error)
+ content(R.string.kau_temp_file_creation_failed)
+ }
+ return@kauRequestPermissions
+ }
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, createUri(c, file))
+ (c as? MediaPickerCore<*>)?.tempPath = file.absolutePath
+ }
+ (c as Activity).startActivityForResult(intent, MEDIA_ACTION_REQUEST_CAMERA)
+ }
+ }
+ }
+}
+
+/**
+ * Basic camera action just for videos
+ */
+class MediaActionCameraVideo(
+ override var color: Int = MediaPickerCore.accentColor
+) : MediaAction {
+ override fun iicon(item: MediaActionItem) = GoogleMaterial.Icon.gmd_videocam
+ override fun invoke(c: Context, item: MediaActionItem) {
+ val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
+ if (intent.resolveActivity(c.packageManager) == null) {
+ c.materialDialog {
+ title(R.string.kau_no_camera_found)
+ content(R.string.kau_no_camera_found_content)
+ }
+ return
+ }
+ (c as Activity).startActivityForResult(intent, MEDIA_ACTION_REQUEST_CAMERA)
+ }
+}
+
+/**
+ * Opens a picker for the type specified in the activity
+ * The type will be added programmatically
+ */
+class MediaActionGallery(
+ val multiple: Boolean = false,
+ override var color: Int = MediaPickerCore.accentColor
+) : MediaAction {
+
+ override fun iicon(item: MediaActionItem) = when (item.mediaType) {
+ MediaType.IMAGE -> if (multiple) GoogleMaterial.Icon.gmd_photo_library else GoogleMaterial.Icon.gmd_photo
+ MediaType.VIDEO -> GoogleMaterial.Icon.gmd_video_library
+ }
+
+ override fun invoke(c: Context, item: MediaActionItem) {
+ c.kauRequestPermissions(PERMISSION_READ_EXTERNAL_STORAGE) {
+ granted, _ ->
+ if (granted) {
+ val intent = Intent().apply {
+ type = item.mediaType.mimeType
+ action = Intent.ACTION_GET_CONTENT
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple)
+ }
+ (c as Activity).startActivityForResult(
+ Intent.createChooser(intent, c.string(R.string.kau_select_media)),
+ MEDIA_ACTION_REQUEST_PICKER)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaItemBasic.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaItemBasic.kt
index c28ed29..e546afb 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaItemBasic.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaItemBasic.kt
@@ -30,12 +30,7 @@ class MediaItemBasic(val data: MediaModel)
fastAdapter.withSelectable(false)
//add image data and return right away
.withOnClickListener { _, _, item, _ ->
- val intent = Intent()
- val data = arrayListOf(item.data)
- intent.putParcelableArrayListExtra(MEDIA_PICKER_RESULT, data)
- activity.setResult(AppCompatActivity.RESULT_OK, intent)
- if (buildIsLollipopAndUp) activity.finishAfterTransition()
- else activity.finish()
+ activity.finish(arrayListOf(item.data))
true
}
}
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaModel.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaModel.kt
index 9188dd6..ae85558 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaModel.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaModel.kt
@@ -27,6 +27,14 @@ data class MediaModel(
cursor.getString(4)
)
+ constructor(f: File) : this(
+ f.absolutePath,
+ f.extension, // this isn't a mime type, but it does give some info
+ f.length(),
+ f.lastModified(),
+ f.nameWithoutExtension
+ )
+
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt
index 2ba6b43..c3b6396 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt
@@ -1,6 +1,5 @@
package ca.allanwang.kau.mediapicker
-import android.content.Intent
import android.database.Cursor
import android.os.Bundle
import android.support.design.widget.AppBarLayout
@@ -21,7 +20,10 @@ import com.mikepenz.google_material_typeface_library.GoogleMaterial
* Images are blurred when selected, and multiple images can be selected at a time.
* Having three layered images makes this slightly slower than [MediaPickerActivityOverlayBase]
*/
-abstract class MediaPickerActivityBase(mediaType: MediaType) : MediaPickerCore<MediaItem>(mediaType) {
+abstract class MediaPickerActivityBase(
+ mediaType: MediaType,
+ mediaActions: List<MediaAction> = emptyList()
+) : MediaPickerCore<MediaItem>(mediaType, mediaActions) {
val coordinator: CoordinatorLayout by bindView(R.id.kau_coordinator)
val toolbar: Toolbar by bindView(R.id.kau_toolbar)
@@ -57,11 +59,7 @@ abstract class MediaPickerActivityBase(mediaType: MediaType) : MediaPickerCore<M
if (selection.isEmpty()) {
toast(R.string.kau_no_items_selected)
} else {
- val intent = Intent()
- val data = ArrayList(selection.map { it.data })
- intent.putParcelableArrayListExtra(MEDIA_PICKER_RESULT, data)
- setResult(RESULT_OK, intent)
- finish()
+ finish(ArrayList(selection.map { it.data }))
}
}
hideOnDownwardsScroll(recycler)
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityOverlayBase.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityOverlayBase.kt
index b5da345..67f9577 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityOverlayBase.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityOverlayBase.kt
@@ -17,7 +17,10 @@ import ca.allanwang.kau.utils.toast
* as opposed to three layers deep
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
-abstract class MediaPickerActivityOverlayBase(mediaType: MediaType) : MediaPickerCore<MediaItemBasic>(mediaType) {
+abstract class MediaPickerActivityOverlayBase(
+ mediaType: MediaType,
+ mediaActions: List<MediaAction> = emptyList()
+) : MediaPickerCore<MediaItemBasic>(mediaType, mediaActions) {
val draggable: ElasticDragDismissFrameLayout by bindView(R.id.kau_draggable)
val recycler: RecyclerView by bindView(R.id.kau_recyclerview)
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt
index 71449d3..6f0241c 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt
@@ -2,12 +2,16 @@ package ca.allanwang.kau.mediapicker
import android.Manifest
import android.app.Activity
+import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Color
import android.graphics.drawable.Drawable
+import android.net.Uri
import android.os.Bundle
+import android.provider.BaseColumns
+import android.provider.DocumentsContract
import android.provider.MediaStore
import android.support.v4.app.LoaderManager
import android.support.v4.content.CursorLoader
@@ -18,15 +22,19 @@ import ca.allanwang.kau.animators.FadeScaleAnimatorAdd
import ca.allanwang.kau.animators.KauAnimator
import ca.allanwang.kau.internal.KauBaseActivity
import ca.allanwang.kau.kotlin.lazyContext
+import ca.allanwang.kau.logging.KL
import ca.allanwang.kau.permissions.kauRequestPermissions
import ca.allanwang.kau.utils.dimenPixelSize
import ca.allanwang.kau.utils.toast
import com.bumptech.glide.Glide
import com.mikepenz.fastadapter.IItem
+import com.mikepenz.fastadapter.adapters.HeaderAdapter
import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.IconicsDrawable
+import com.mikepenz.iconics.typeface.IIcon
import org.jetbrains.anko.doAsync
+import java.io.File
import java.util.concurrent.ExecutionException
import java.util.concurrent.Future
@@ -35,7 +43,10 @@ import java.util.concurrent.Future
*
* Container for the main logic behind the both pickers
*/
-abstract class MediaPickerCore<T : IItem<*, *>>(val mediaType: MediaType) : KauBaseActivity(), LoaderManager.LoaderCallbacks<Cursor> {
+abstract class MediaPickerCore<T : IItem<*, *>>(
+ val mediaType: MediaType,
+ val mediaActions: List<MediaAction>
+) : KauBaseActivity(), LoaderManager.LoaderCallbacks<Cursor> {
companion object {
val viewSize = lazyContext { computeViewSize(it) }
@@ -62,11 +73,13 @@ abstract class MediaPickerCore<T : IItem<*, *>>(val mediaType: MediaType) : KauB
/**
* Create error tile for a given item
*/
- fun getErrorDrawable(context: Context): Drawable {
+ fun getErrorDrawable(context: Context) = getIconDrawable(context, GoogleMaterial.Icon.gmd_error, accentColor)
+
+ fun getIconDrawable(context: Context, iicon: IIcon, color: Int): Drawable {
val sizePx = MediaPickerCore.computeViewSize(context)
- return IconicsDrawable(context, GoogleMaterial.Icon.gmd_error)
+ return IconicsDrawable(context, iicon)
.sizePx(sizePx)
- .backgroundColor(accentColor)
+ .backgroundColor(color)
.paddingPx(sizePx / 3)
.color(Color.WHITE)
}
@@ -101,6 +114,9 @@ abstract class MediaPickerCore<T : IItem<*, *>>(val mediaType: MediaType) : KauB
val extraSpace: Int by lazy { resources.displayMetrics.heightPixels }
fun initializeRecycler(recycler: RecyclerView) {
+ val adapterWrapper = HeaderAdapter<MediaActionItem>()
+ adapterWrapper.wrap(adapter)
+ adapterWrapper.add(mediaActions.map { MediaActionItem(it, mediaType) })
recycler.apply {
val manager = object : GridLayoutManager(context, computeColumnCount(context)) {
override fun getExtraLayoutSpace(state: RecyclerView.State?): Int {
@@ -110,7 +126,7 @@ abstract class MediaPickerCore<T : IItem<*, *>>(val mediaType: MediaType) : KauB
setItemViewCacheSize(CACHE_SIZE)
isDrawingCacheEnabled = true
layoutManager = manager
- adapter = this@MediaPickerCore.adapter
+ adapter = adapterWrapper
setHasFixedSize(true)
itemAnimator = KauAnimator(FadeScaleAnimatorAdd(0.8f))
}
@@ -209,4 +225,87 @@ abstract class MediaPickerCore<T : IItem<*, *>>(val mediaType: MediaType) : KauB
prefetcher?.cancel(true)
super.onDestroy()
}
+
+ /**
+ * Method used to retrieve uri data for API 19+
+ * See <a href="http://hmkcode.com/android-display-selected-image-and-its-real-path/"></a>
+ */
+ private fun <R> ContentResolver.query(baseUri: Uri, uris: List<Uri>, block: (cursor: Cursor) -> R) {
+ val ids = uris.map {
+ DocumentsContract.getDocumentId(it).split(":").getOrNull(1)
+ }.filterNotNull().joinToString(prefix = "(", separator = ",", postfix = ")")
+ //? query replacements are done for one arg at a time
+ //since we potentially have a list of ids, we'll just format the WHERE clause ourself
+ query(baseUri, MediaModel.projection, "${BaseColumns._ID} IN $ids", null, sortQuery)?.use(block)
+ }
+
+ internal var tempPath: String? = null
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (resultCode != RESULT_OK) {
+ if (tempPath != null) {
+ val f = File(tempPath)
+ if (f.exists()) f.delete()
+ tempPath = null
+ }
+ return super.onActivityResult(requestCode, resultCode, data)
+ }
+ KL.d("Media result received")
+ if (data == null) {
+ KL.d("Media null intent")
+ return super.onActivityResult(requestCode, resultCode, data)
+ }
+ when (requestCode) {
+ MEDIA_ACTION_REQUEST_CAMERA -> onCameraResult(data)
+ MEDIA_ACTION_REQUEST_PICKER -> onPickerResult(data)
+ else -> super.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ private fun onCameraResult(data: Intent) {
+ val f: File
+ if (tempPath != null) {
+ f = File(tempPath)
+ tempPath = null
+ } else if (data.data != null) {
+ f = File(data.data.path)
+ } else {
+ KL.d("Media camera no file found")
+ return
+ }
+ if (f.exists()) {
+ KL.v("Media camera path found", f.absolutePath)
+ scanMedia(f)
+ finish(arrayListOf(MediaModel(f)))
+ } else {
+ KL.d("Media camera file not found")
+ }
+ }
+
+ private fun onPickerResult(data: Intent) {
+ val items = mutableListOf<Uri>()
+ if (data.data != null) {
+ KL.v("Media picker data uri", data.data.path)
+ items.add(data.data)
+ } else {
+ val clip = data.clipData
+ if (clip != null) {
+ items.addAll((0 until clip.itemCount).map {
+ clip.getItemAt(it).uri.apply {
+ KL.v("Media picker clip uri", path)
+ }
+ })
+ }
+ }
+ if (items.isEmpty()) return KL.d("Media picker empty intent")
+ contentResolver.query(mediaType.contentUri, items) {
+ if (it.moveToFirst()) {
+ val models = arrayListOf<MediaModel>()
+ do {
+ models.add(MediaModel(it))
+ } while (it.moveToNext())
+ finish(models)
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaType.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaType.kt
index cfec331..0af4c2e 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaType.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaType.kt
@@ -7,7 +7,17 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
/**
* Created by Allan Wang on 2017-07-30.
*/
-enum class MediaType(val cacheStrategy: DiskCacheStrategy, val contentUri: Uri) {
- IMAGE(DiskCacheStrategy.AUTOMATIC, MediaStore.Images.Media.EXTERNAL_CONTENT_URI),
- VIDEO(DiskCacheStrategy.AUTOMATIC, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
+enum class MediaType(val cacheStrategy: DiskCacheStrategy,
+ val mimeType: String,
+ val captureType: String,
+ val contentUri: Uri) {
+ IMAGE(DiskCacheStrategy.AUTOMATIC,
+ "image/*",
+ MediaStore.ACTION_IMAGE_CAPTURE,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI),
+
+ VIDEO(DiskCacheStrategy.AUTOMATIC,
+ "video/*",
+ MediaStore.ACTION_VIDEO_CAPTURE,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
} \ No newline at end of file
diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaUtils.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaUtils.kt
new file mode 100644
index 0000000..0fb5824
--- /dev/null
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaUtils.kt
@@ -0,0 +1,59 @@
+package ca.allanwang.kau.mediapicker
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Environment
+import android.support.v7.app.AppCompatActivity
+import ca.allanwang.kau.utils.buildIsLollipopAndUp
+import com.mikepenz.fastadapter.IItem
+import java.io.File
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+/**
+ * Created by Allan Wang on 2017-08-17.
+ */
+@SuppressLint("NewApi")
+internal fun Activity.finish(data: ArrayList<MediaModel>) {
+ val intent = Intent()
+ intent.putParcelableArrayListExtra(MEDIA_PICKER_RESULT, data)
+ setResult(AppCompatActivity.RESULT_OK, intent)
+ if (buildIsLollipopAndUp) finishAfterTransition()
+ else finish()
+}
+
+@Throws(IOException::class)
+fun createMediaFile(prefix: String, extension: String): File {
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
+ val imageFileName = "${prefix}_${timeStamp}_"
+ val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ val frostDir = File(storageDir, prefix)
+ if (!frostDir.exists()) frostDir.mkdirs()
+ return File.createTempFile(imageFileName, extension, frostDir)
+}
+
+@Throws(IOException::class)
+fun Context.createPrivateMediaFile(prefix: String, extension: String): File {
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
+ val imageFileName = "${prefix}_${timeStamp}_"
+ val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
+ return File.createTempFile(imageFileName, extension, storageDir)
+}
+
+/**
+ * Scan the path so that the media item is properly added to galleries
+ *
+ * See <a href="https://developer.android.com/training/camera/photobasics.html#TaskGallery">Docs</a>
+ */
+fun Context.scanMedia(f: File) {
+ if (!f.exists()) return
+ val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
+ val contentUri = Uri.fromFile(f)
+ mediaScanIntent.data = contentUri
+ sendBroadcast(mediaScanIntent)
+} \ No newline at end of file
diff --git a/mediapicker/src/main/res/values/ids.xml b/mediapicker/src/main/res/values/ids.xml
new file mode 100644
index 0000000..a533ecc
--- /dev/null
+++ b/mediapicker/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="kau_item_media_action" type="id" />
+</resources> \ No newline at end of file
diff --git a/mediapicker/src/main/res/values/strings.xml b/mediapicker/src/main/res/values/strings.xml
index 39ab16b..717e12b 100644
--- a/mediapicker/src/main/res/values/strings.xml
+++ b/mediapicker/src/main/res/values/strings.xml
@@ -4,4 +4,11 @@
<string name="kau_no_items_selected">No items have been selected</string>
<string name="kau_blurrable_imageview">Blurrable ImageView</string>
<string name="kau_no_items_loaded">No items loaded</string>
+
+ <string name="kau_no_camera_found">No camera found</string>
+ <string name="kau_no_camera_found_content">Please install a camera app and try again.</string>
+
+ <string name="kau_temp_file_creation_failed">Failed to create a temporary file.</string>
+
+ <string name="kau_select_media">Select Media</string>
</resources> \ No newline at end of file
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index ee6cc86..d8bbe51 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".SampleApp"
@@ -46,6 +47,16 @@
<activity
android:name=".AdapterActivity"
android:theme="@style/Kau.Translucent.SlideBottom" />
+
+ <provider
+ android:name="android.support.v4.content.FileProvider"
+ android:authorities="${applicationId}.provider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/kau_file_paths" />
+ </provider>
</application>
</manifest> \ No newline at end of file
diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
index 5d0bd36..09002d4 100644
--- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
+++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
@@ -169,6 +169,10 @@ class MainActivity : KPrefActivity() {
onClick = { _, _, _ -> startActivityWithEdge(SWIPE_EDGE_LEFT); false }
}
+ plainText(R.string.image_showcase) {
+ onClick = { _, _, _ -> kauLaunchMediaPicker(ImagePickerActivity::class.java, REQUEST_MEDIA); false }
+ }
+
plainText(R.string.video_overlay_showcase) {
onClick = { _, _, _ -> kauLaunchMediaPicker(VideoPickerActivityOverlay::class.java, REQUEST_MEDIA); false }
}
diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MediaPicker.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MediaPicker.kt
index 7aac0ef..618ca07 100644
--- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MediaPicker.kt
+++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MediaPicker.kt
@@ -1,16 +1,28 @@
package ca.allanwang.kau.sample
-import ca.allanwang.kau.mediapicker.MediaPickerActivityBase
-import ca.allanwang.kau.mediapicker.MediaPickerActivityOverlayBase
-import ca.allanwang.kau.mediapicker.MediaType
+import android.content.Context
+import android.net.Uri
+import android.support.v4.content.FileProvider
+import ca.allanwang.kau.mediapicker.*
+import java.io.File
/**
* Created by Allan Wang on 2017-07-23.
*/
-class ImagePickerActivity : MediaPickerActivityBase(MediaType.IMAGE)
+private fun actions(multiple: Boolean) = listOf(object : MediaActionCamera() {
-class ImagePickerActivityOverlay : MediaPickerActivityOverlayBase(MediaType.IMAGE)
+ override fun createFile(context: Context): File
+ = createMediaFile("KAU", ".jpg")
-class VideoPickerActivity : MediaPickerActivityBase(MediaType.VIDEO)
+ override fun createUri(context: Context, file: File): Uri
+ = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
-class VideoPickerActivityOverlay : MediaPickerActivityOverlayBase(MediaType.VIDEO) \ No newline at end of file
+}, MediaActionGallery(multiple))
+
+class ImagePickerActivity : MediaPickerActivityBase(MediaType.IMAGE, actions(true))
+
+class ImagePickerActivityOverlay : MediaPickerActivityOverlayBase(MediaType.IMAGE, actions(false))
+
+class VideoPickerActivity : MediaPickerActivityBase(MediaType.VIDEO, actions(true))
+
+class VideoPickerActivityOverlay : MediaPickerActivityOverlayBase(MediaType.VIDEO, actions(false)) \ No newline at end of file
diff --git a/sample/src/main/res/xml/kau_file_paths.xml b/sample/src/main/res/xml/kau_file_paths.xml
new file mode 100644
index 0000000..83b00bb
--- /dev/null
+++ b/sample/src/main/res/xml/kau_file_paths.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+ <external-path
+ name="KAU_images"
+ path="Android/data/ca.allanwang.kau.sample/files/Pictures" />
+ <external-path
+ name="KAU_public_images"
+ path="Pictures/KAU" />
+</paths> \ No newline at end of file