『Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린』 교재를 바탕으로 정리한 내용입니다.
콘텐츠 프로바이더란?
앱끼리 데이터를 연동하는 컴포넌트
(어떤 앱의 데이터를 다른 앱에서 이용할 수 있게 하려면 콘텐츠 프로바이더를 이용)
class MyContentProvider : ContentProvider() {
override fun onCreate(): Boolean {
return false
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return null
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
}
<provider
android:authorities="com.example.test_provider"
android:name=".MyContentProvider"
android:enabled="true"
android:exported="true"></provider>
콘텐츠 프로바이더는 필요한 순간에 시스템에서 자동으로 생성해주므로 인텐트와 상관없이 query(), insert(), update(), delete() 함수만 호출해 주면 사용 가능
외부 앱에서 사용하기 위해 메니페스트에 해당 앱에 대한 패키지 공개 설정
<queries>
<!-- 둘 중 하나만 선언. -->
<!-- <provider android:authorities="com.example.provider" />. -->
<package android:name="com.example.test_outter" />
</queries>
contentResolver.query(
Uri.parse("content://com.example.test_provider"), null, null, null, null
)
public final int delete(Uri url, String where, String[] selectionArgs)
public final Uri insert(Uri url, ContentValues values)
public final Cursor query(Uri url, String[] projection, String selection)
public final int update(Uri url, ContentValues values, String where, String[] selectionArgs)
→ 첫 번째 매개변수 Uri 객체 : 프로토콜명과 콘텐츠 프로바이더 식별자로 등록된 authorities 값이어야 함
→ inset()함수와 upddate() 함수의 매개변수인 ContentValues는 Map 형태의 집합 객체
<uses-permission android:name="android.permission.READ_CONTACTS" />
val intent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
getResult.launch(intent)
→ Uri 객체는 Uri.parse() 함수로 직접 지정하거나 아래와 같은 상수 이용
- ContactsContract.Contacts.CONTENT_URI : 모든 사람의 데이터
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI : 전화번호가 있는 사람
- ContactsContract.CommonDataKinds.Email.CONTENT_URI : 이메일 정보가 있는 사람
private val getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if(it.resultCode == RESULT_OK) {
val cursor = contentResolver.query(
it.data!!.data!!,
arrayOf<String>( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER),null, null, null
)
Log.d("kkang", "cursor size....${cursor?.count}")
if (cursor!!.moveToFirst()) {
val name = cursor?.getString(0)
val phone = cursor?.getString(1)
}
}
}
//옵션 지정하지 않고 비트맵 생성
val bitmap = BitmapFactory.decodeStream(inputStream)
//옵션 지정해 비트맵 생성
val option = BitmapFactory.Options()
option.inSampleSize = 4
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
getResult.launch(intent) //getResult -> 인텐트 콜백 함수명
private fun calculateInSampleSize(fileUri: Uri, reqWidth: Int, reqHeight: Int): Int {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true //옵션만 설정하기 위해 true로 지정
try {
var inputStream = contentResolver.openInputStream(fileUri)
BitmapFactory.decodeStream(inputStream, null, options)
inputStream!!.close()
inputStream = null
} catch (e: Exception) {
e.printStackTrace()
}
val(height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
//inSampleSize 비율 계산
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
private val getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if(it.resultCode == RESULT_OK) {
try {
//inSampleSize 비율 계산
val calRatio = calculateInSampleSize(it.data!!.data!!,
resources.getDimensionPixelSize(R.dimen.imgSize),
resources.getDimensionPixelSize(R.dimen.imgSize))
val option = BitmapFactory.Options()
option.inSampleSize = calRatio
//이미지 불러오기
var inputStream = contentResolver.openInputStream(it.data!!.data!!)
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)
inputStream!!.close()
inputStream = null
bitmap?.let {
binding.galleryResult.setImageBitmap(bitmap)
}?: let {
Log.d("kkang", "bitmap null")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
getResult.launch(intent) //getResult -> 인텐트 콜백 함수명
val bitmap = data?.getExtras()?.get("data") as Bitmap
사진 파일을 공유하는 방법
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
→ API 레벨 24 버전부터는 file:// 프로토콜로 구성된 URI를 외부에 노출하지 못하도록 하는 엄격 모드가 적용됨. 따라서, 앱끼리 파일을 공유하려면 content:// 프로토콜을 이용하고 이 URI에 임시로 접근할 수 있는 권한을 부여해야 함.
→ 이 때, FileProvider 클래스 이용해 XML 설정을 기반으로 content:// 프로토콜 URI를 생성해 줌
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="myfiles"
path="Android/data/com.example.chap16/files/Pictures" />
</paths>
<provider
android:authorities="com.example.chap16.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
val timestamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File.createTempFile(
"JPEG_${timestamp}_",
".jpg",
storageDir
)
filePath = file.absolutePath
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
getResult.launch(intent) //getResult -> 인텐트 콜백 함수명
val option = BitmapFactory.Options()
option.inSampleSize = 10
val bitmap = BitmapFactory.decodeFile(filePath, option)
bitmap?.let {
binding.cameraResult.setImageBitmap(bitmap)
}
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.5662952,126.9779451"))
getResult.launch(intent) //getResult -> 인텐트 콜백 함수명
//퍼미션 설정
<uses-permission android:name="android.permission.CALL_PHONE" />
val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:02-120"))
getResult.launch(intent) //getResult -> 인텐트 콜백 함수명