Kotlin : 사진 등록 처리
2023. 8. 25. 00:38ㆍ[Android APP] feat. Kotlin/Kotlin 공부
아이템을 추가한 뒤 그 아이템에 제목과 정보를 넣어주고 사진을 첨부할 수 있도록 만들었다.
1. 기존 이미지 조회 후, 사진 등록 버튼 클릭 시 pickPicture 함수 실행
//이미지 조회(pick Picture 함수를 실행 시키면 사진 편집이 가능하다)
binding.imageRecycler.apply {
layoutManager = LinearLayoutManager(this@WorkEditPhoto)
adapter = OutsideAdapter(this@WorkEditPhoto, userToken, sysDocNum, consCode, imageOutsideData, {deleteItem(it)}, {pickPicture(it)})
}
2. 새로운 리사이클러 아이템 추가
//아이템 추가 버튼
binding.addItemBtn.setOnClickListener {
addItem()
}
//아이템 추가
@SuppressLint("NotifyDataSetChanged")
fun addItem() {
val division = arrayListOf<InsideImageInfoList>()
imageOutsideInputData = OutSideImageInfo(defaultTitle, division)
imageOutsideData.add(imageOutsideInputData)
binding.imageRecycler.adapter?.notifyDataSetChanged()
}
3. 사진 등록 클릭 시(picPicture 함수)
//사진 선택 다이어로그
private fun pickPicture(position : Int){
val dialog = Dialog(this@WorkEditPhoto)
dialog.setContentView(R.layout.custom_dialog_camera)
val text =dialog.findViewById<TextView>(R.id.camera_title)
val camera = dialog.findViewById<Button>(R.id.camera_btn)
val album = dialog.findViewById<Button>(R.id.album_btn)
text.text = "사진 등록"
camera.setOnClickListener {
dispatchTakePictureIntentEx(position)
dialog.dismiss()
}
album.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
positions = position
Log.d("position_value", position.toString())
startActivityForResult(intent, CodeList.Album)
dialog.dismiss()
}
dialog.window!!.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)
dialog.setCanceledOnTouchOutside(true)
dialog.setCancelable(true)
dialog.show()
}
4. 카메라 호출 함수(camera로 등록 클릭 시)
//카메라 호출 함수
@SuppressLint("SimpleDateFormat")
private fun dispatchTakePictureIntentEx(position: Int)
{
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val uri : Uri? = createImageUri("JPEG_${timeStamp}")
photoURI = uri
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
positions = position
startActivityForResult(takePictureIntent, CodeList.Camera)
}
카메라 호출 후 사진 저장(jpeg 형태로 저장)
//카메라 사진 저장 함수
private fun createImageUri(filename:String): Uri?{
val values = ContentValues()
values.put(MediaStore.Images.Media.DISPLAY_NAME,filename)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
return contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
}
5. 데이터 삭제(data로 imageOutsideData 리스트의 OutsideImageInfo 데이터를 삭제해준다.)
//사진 리스트 삭제(Outside)
@SuppressLint("NotifyDataSetChanged")
private fun deleteItem(data : OutSideImageInfo){
imageOutsideData.remove(data)
binding.imageRecycler.adapter?.notifyDataSetChanged()
}
6. 앨범, 카메라 처리(multiple 처리를 통해 사진 여러장을 동시에 등록할 수 있다)
@SuppressLint("NotifyDataSetChanged")
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != Activity.RESULT_OK) {
return
}
when (requestCode) {
//사진 - 파일 탐색기에서 가져오기
CodeList.Album -> {
data ?: return
val clipData = data.clipData
Log.d("position", positions.toString())
if (clipData == null || clipData.itemCount == 1) {
// Single photo selected
val uri = if (clipData != null)
clipData.getItemAt(0).uri
else data.data
val filename = getName(uri)
val imgPath = usiPathUtil.getRealPathFromURI(this, uri!!)
if (filename != "" && imgPath != null) {
val `in` = contentResolver.openInputStream(uri)
val file = File(applicationContext.filesDir, filename)
if (`in` != null) {
try {
val out: OutputStream = FileOutputStream(file)
try {
val buf = ByteArray(4096)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
val title = imageOutsideData[positions].parentTitle //position은 현재 등록할(선택된) 아이템의 인덱스를 의미한다.
imageOutsideData[positions].fileList.add(InsideImageInfoList(imageOutsideData[positions].fileList.size, filename, "", file.path, title,file))
binding.imageRecycler.adapter?.notifyDataSetChanged()
} finally {
out.close()
}
} finally {
`in`.close()
}
}
}
} else {
// Multiple photos selected
for (i in 0 until clipData.itemCount) {
val uri = clipData.getItemAt(i).uri
Log.d("position", positions.toString())
val filename = getName(uri)
val imgPath = usiPathUtil.getRealPathFromURI(this, uri)
if (filename != "" && imgPath != null) {
val `in` = contentResolver.openInputStream(uri)
val file = File(applicationContext.filesDir, filename)
if (`in` != null) {
try {
val out: OutputStream = FileOutputStream(file)
try {
val buf = ByteArray(4096)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
val title = imageOutsideData[positions].parentTitle
imageOutsideData[positions].fileList.add(InsideImageInfoList(imageOutsideData[positions].fileList.size, filename, "", file.path, title, file))
binding.imageRecycler.adapter?.notifyDataSetChanged()
} finally {
out.close()
}
} finally {
`in`.close()
}
}
}
}
}
}
//사진 - 카메라에서 가져오기
CodeList.Camera -> {
if( photoURI != null)
{
val filename = getName(photoURI)
val imgPath = usiPathUtil.getRealPathFromURI(this, photoURI as Uri)
if (filename != "" && imgPath != null) {
Log.d("position", positions.toString())
val `in` = contentResolver.openInputStream(photoURI as Uri) //src
val file = File(applicationContext.filesDir, filename)
if (`in` != null) {
try {
val out: OutputStream = FileOutputStream(file) //dst
try {
// Transfer bytes from in to out
val buf = ByteArray(4096)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
val title = imageOutsideData[positions].parentTitle
imageOutsideData[positions].fileList.add(InsideImageInfoList(imageOutsideData[positions].fileList.size ,filename , "",file.path, title, file))
binding.imageRecycler.adapter?.notifyDataSetChanged()
} finally {
out.close()
}
} finally {
`in`.close()
}
}
}
}
}
}
}
//Uri to 파일명 추출 함수
private fun getName(uri: Uri?): String {
val projection = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME)
val cursor = managedQuery(uri, projection, null, null, null)
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
cursor.moveToFirst()
return cursor.getString(columnIndex)
}
7. 아이템과 사진을 모두 등록한 뒤 최종 등록 처리
//등록 버튼
binding.bottomPostBtn.setOnClickListener {
Toast.makeText(this@WorkEditPhoto, "사진이 등록중입니다.", Toast.LENGTH_SHORT).show()
CoroutineScope(Dispatchers.IO).launch {
compareList(imageOriginData, imageOutsideData)
delay(2000)
val intent = Intent(this@WorkEditPhoto, DailyWorkDocument::class.java)
intent.putExtra("userId", userId)
intent.putExtra("sysDocNum", sysDocNum)
intent.putExtra("code", consCode)
startActivity(intent)
finish() }
}
8.
package com.allscapeservice.a22allscape_app.objects
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.util.Log
import android.webkit.MimeTypeMap
import android.widget.Toast
import java.io.File
import java.io.FileOutputStream
class UriPathUtils
{
fun getRealPathFromURI(context: Context, uri: Uri): String? {
when {
// DocumentProvider
DocumentsContract.isDocumentUri(context, uri) -> {
when {
// ExternalStorageProvider
isExternalStorageDocument(uri) -> {
//Toast.makeText(context, "From Internal & External Storage dir", Toast.LENGTH_SHORT).show()
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
// This is for checking Main Memory
return if ("primary".equals(type, ignoreCase = true)) {
if (split.size > 1) {
Environment.getExternalStorageDirectory().toString() + "/" + split[1]
} else {
Environment.getExternalStorageDirectory().toString() + "/"
}
// This is for checking SD Card
} else {
"storage" + "/" + docId.replace(":", "/")
}
}
isDownloadsDocument(uri) -> {
//Toast.makeText(context, "From Downloads dir", Toast.LENGTH_SHORT).show()
val fileName = getFilePath(context, uri)
if (fileName != null) {
return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
}
var id = DocumentsContract.getDocumentId(uri)
if (id.startsWith("raw:")) {
id = id.replaceFirst("raw:".toRegex(), "")
val file = File(id)
if (file.exists()) return id
}
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
return getDataColumn(context, contentUri, null, null)
}
isMediaDocument(uri) -> {
//Toast.makeText(context, "From Documents dir", Toast.LENGTH_SHORT).show()
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
var contentUri: Uri? = null
when (type) {
"image" -> {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
"video" -> {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
"audio" -> {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
"document" -> {
val fileName = getFilePath(context, uri)
if (fileName != null) {
return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
}
var id = DocumentsContract.getDocumentId(uri)
if (id.startsWith("raw:")) {
id = id.replaceFirst("raw:".toRegex(), "")
val file = File(id)
if (file.exists()) return id
}
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
return getDataColumn(context, contentUri, null, null)
}
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
}
isGoogleDriveUri(uri) -> {
Toast.makeText(context, "From Google Drive", Toast.LENGTH_SHORT).show()
return getDriveFilePath(uri, context)
}
}
}
"content".equals(uri.scheme, ignoreCase = true) -> {
//Toast.makeText(context, "From Content Uri", Toast.LENGTH_SHORT).show()
// Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
}
"file".equals(uri.scheme, ignoreCase = true) -> {
//Toast.makeText(context, "From File Uri", Toast.LENGTH_SHORT).show()
return uri.path
}
}
return null
}
private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
if (uri == null) return null
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
//여기서부터 갈림
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
} finally {
cursor?.close()
}
return null
}
//파일 확장자 함수
fun getFileExtension(context : Context, uri: Uri): String? {
val fileType: String? = context.contentResolver.getType(uri)
return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType)
}
private fun getMediaDocumentPath(context: Context, uri: Uri?,selection: String?, selectionArgs: Array<String>?): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
if (uri == null) return null
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
} finally {
cursor?.close()
}
return null
}
private fun getFilePath(context: Context, uri: Uri?): String? {
var cursor: Cursor? = null
val projection = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME)
try {
if (uri == null) return null
cursor = context.contentResolver.query(uri, projection, null, null,
null)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
return cursor.getString(index)
}
} finally {
cursor?.close()
}
return null
}
private fun getDriveFilePath(uri: Uri, context: Context): String? {
val returnCursor = context.contentResolver.query(uri, null, null, null, null)
val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
// val sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE)
returnCursor.moveToFirst()
val name = returnCursor.getString(nameIndex)
//val size = returnCursor.getLong(sizeIndex).toString()
val file = File(context.cacheDir, name)
try {
val inputStream = context.contentResolver.openInputStream(uri)
val outputStream = FileOutputStream(file)
var read = 0
val maxBufferSize = 1 * 1024 * 1024
val bytesAvailable = inputStream!!.available()
//int bufferSize = 1024;
val bufferSize = Math.min(bytesAvailable, maxBufferSize)
val buffers = ByteArray(bufferSize)
while (inputStream.read(buffers).also { read = it } != -1) {
outputStream.write(buffers, 0, read)
}
inputStream.close()
outputStream.close()
} catch (e: Exception) {
Log.e("Exception", e.message!!)
}
return file.path
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
private fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
private fun isGoogleDriveUri(uri: Uri): Boolean {
return "com.google.android.apps.docs.storage" == uri.authority || "com.google.android.apps.docs.storage.legacy" == uri.authority
}
}
안드로이드에서 카메라 사용 시 서버로 사진파일을 전송할 때(저장할 때) 썸네일이 들어가 화질이 깨지는 문제와
사진이 회전되서 저장되는 문제도 고려해야한다. 이 문제를 해결하기 위해 조금 시간을 썼었던 기억이 난다.
반응형
'[Android APP] feat. Kotlin > Kotlin 공부' 카테고리의 다른 글
kotlin : bitmap 크기 조절하기 (0) | 2023.08.25 |
---|---|
Kotlin : RecyclerView snapHelper (0) | 2023.03.27 |
Kotlin : RecyclerView item EditText 중복 에러 해결 (0) | 2023.03.27 |
Kotlin : 명시적 인텐트(Explicit Intent), 암시적 인텐트(Implicit Intent) (0) | 2023.02.27 |
Kotlin : Lottie Library(애니메이션 적용) (0) | 2023.02.22 |