π SeSACμ 'JetPackκ³Ό Kotlinμ νμ©ν Android App κ°λ°' κ°μ’λ₯Ό μ 리ν κΈ μ λλ€.
ꡬκΈμ κΈ°λ³Έ μ± μ°λμ μν΄μλ ContentProvider
λΌλ μ»΄ν¬λνΈκ° νμνλ€.
λ΄κ° λ§λ μ±μμ μ°λ½μ², κ°€λ¬λ¦¬ λ±μ μλ λ°μ΄ν°κ° νμλ‘ νλ κ²½μ°κ° μλ€. λΉμ°νκ²λ μΈλΆ μ±μμ λ°μ΄ν°μ μ§μ μ κ·Όνλ κ²μ κΈμ§λμ΄ μλ€. κ·Έλμ λ°μ΄ν°λ₯Ό κ°μ§κ³ μλ μ±μ ContentProvider
λ₯Ό λ§λ€κ³ , λ°μ΄ν°λ₯Ό μ΄μ©νλ μΈλΆ μ±μμ ContentProvider
μ μ΄μ©νλ€.
class MyContentProvider : ContentProvider() {
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int { }
override fun getType(uri: Uri): String? { }
override fun insert(uri: Uri, values: ContentValues?): Uri? { }
override fun onCreate(): Boolean { }
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? { }
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int { }
컨ν
νΈ νλ‘λ°μ΄λλ ContentProvider
λ₯Ό μμ λ°μ μμ±
API ννλ§ Sql λ¬Έκ³Ό λΉμ·ν κ² λΏμ΄λ€. νμΌμ read νλ DBλ₯Ό μ°Έμ‘°νλ νκ³ μΆμ λλ‘ κ΅¬ννλ©΄ λλ€.
μΈλΆ μ±μμ query()
ν¨μλ₯Ό νΈμΆνλ©΄ λ΄λΆ μ±μ λ°μ΄ν°λ₯Ό μ λ¬νλ€. μΈλΆ μ±μμ delete()
ν¨μλ‘ λ΄λΆ μ±μ λ°μ΄ν°λ₯Ό μμ ν μλ μλ€. μ΄κ²μ νμ©νκ³ μΆμ§ μλ€λ©΄ κ·Έλ₯ ν¨μ λ΄λΆλ₯Ό ꡬννμ§ μμΌλ©΄ λλ€. μ μ μΌλ‘ κ°λ°μ λ§μμ΄λ€.
<provider
android:name=".MyContentProvider"
android:authorities="com.example.provider"
android:enabled="true"
android:exported="true></provider>
컨ν
νΈ νλ‘λ°μ΄λλ νλμ μ»΄ν¬λνΈ
μ΄λ€. κ·Έλμ λΉμ°ν λ§€λνμ€νΈ νμΌμ λ±λ‘ν΄μΌ νλ€.
<provider>
νκ·Έλ‘ λ±λ‘νλ€.
β λ€λ₯Έ μ»΄ν¬λνΈλ <name>
μμ±λ§ νμμ§λ§, 컨ν
νΈ νλ‘λ°μ΄λλ <authorities>
μμ±λ νμμ΄λ€. μ΄ μμ±κ°μΌλ‘ μλ³λλ€.
컨ν νΈ νλ‘λ°μ΄λλ₯Ό μ΄μ©νκΈ° μν΄μ μΈν νΈλ₯Ό λ°μμν€μ§ μλλ€.
컨ν νΈ νλ‘λ°μ΄λλ νμν μκ° μμ€ν μμ μλμΌλ‘ μμ±.
컨ν νΈ νλ‘λ°μ΄λλ₯Ό μ΄μ©νκ³ μ νλ μ±μ query(), insert(), delete(), update() ν¨μλ§ νΈμΆ.
4κ°μ μ»΄ν¬λνΈ μ€μμ 컨ν νΈ νλ‘λ°μ΄λλ§μ μΈν νΈ λ§€μ»€λμ¦κ³Ό μ ν κ΄λ ¨μ΄ μλ€. μλνλ©΄ μμ€ν μμ 컨ν νΈ νλ‘λ°μ΄λμ μΈν νΈλ₯Ό λ³κ°λ‘ κ΄λ¦¬νκΈ° λλ¬Έμ΄λ€.
Aμ±μμ Bμ±μ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό νΈμΆνλ©΄, Bμ±μ μμ€ν μμ μλμΌλ‘ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό μμ±νλ€. κ·Έλ¦¬κ³ Aμ±μμ delete(), insert(), query(), update() ν¨μλ₯Ό νΈμΆνλ©΄ λλ€.
νμ§λ§ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό μ΄μ©νκΈ° μν΄μλ μ΄μ©νλ μͺ½μ μ±μ λ©μΈ νκ²½νμΌ μ€μ μ΄ νμνλ€.
<queries>
<!-- λ μ€ νλλ§ μ μΈλμ΄ μμΌλ©΄ λλ€.-->
<!-- <provider android:authorities="com.example.test.provider"/>-->
<package android:name="com.example.test.outter"/>
</queries>
μΈλΆ μ±μ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό μ΄μ©νκ³ μ νλ€λ©΄ ν΄λΉ μ±μ μ΄μ©νκΈ° μν Query Visibility κ΄λ ¨ μ€μ
컨ν
νΈ νλ‘λ°μ΄λλ₯Ό κ°μ§κ³ μλ μ±μ ν¨ν€μ§λͺ
μ <package>
νκ·Έλ‘ νΉμ 컨ν
νΈ νλ‘λ°μ΄λμ authorities λ¬Έμμ΄μ <provider>
νκ·Έλ‘ μ μΈ
contentResolver.query(
Uri.parse("content://com.example.test.provider"),
null, null, null, null)
μμ€ν
μ 컨ν
νΈ νλ‘λ°μ΄λλ₯Ό μ΄μ©νκΈ° μν κ°μ²΄κ° ContentResolver κ°μ²΄
ContentResolver κ°μ²΄λ contentResolver νλ‘νΌν°λ‘ νλνμ¬ query(), insert(), update(), delete() ν¨μλ₯Ό νΈμΆ
첫 λ²μ§Έ λ§€κ°λ³μλ‘ Uri κ°, μλ³μλ₯Ό μ€μ μ΄λ μ±μ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό νΈμΆ ν μ§ μ§μ νλ€.
컨ν νΈ νλ‘λ°μ΄λλ₯Ό μλ³νκΈ° μν Uri κ°μ²΄
νλ‘ν μ½μ content
κ° λμ΄μΌ νκ³ , λλ©μΈ λΆλΆμ 컨ν
νΈ νλ‘λ°μ΄λμ μλ³μ
κ° λμ΄μΌ νλ€.
μ£Όμλ‘μ μ΄μ©νκΈ° μν΄μλ νΌλ―Έμ μ μΈμ΄ νμνλ€.
<uses-permission android:name="android.permission.READ_CONTACTS"/>
μ£Όμλ‘ μ±μ μ°λνμ¬ μ£Όμλ‘ λͺ©λ‘ νλ©΄μ λμ°κΈ°
μ μ κ° μ νν μ¬λμ μ νλ²νΈ νΉμ μ΄λ©μΌ μ 보λ₯Ό νλ
μ£Όμλ‘ λͺ©λ‘ νλ©΄μ μ£Όμλ‘ μ±μ μ‘ν°λΉν°
κ° λλ€. κ·Έλμ μ£Όμλ‘ νλ©΄μ λμ°λ μμ
μ μΈν
νΈλ₯Ό λ°μμμΌμΌ νλ€.
μ νν λ°μ΄ν°λ₯Ό νλνλ μμ
μ μ£Όμλ‘ μ±μ 컨ν
νΈ νλ‘λ°μ΄λ
κ° λλ€.
val intent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
requestActivity.launch(intent)
μ£Όμλ‘μ λͺ©λ‘ νλ©΄μ λμ°κΈ°
μΈν νΈλ₯Ό λ°μμμΌμΌ νλ€.
μ΄ λ λμ Έμ£Όλ URI
μ λ°λΌ μΆλ ₯λλ λ°μ΄ν°κ° λ¬λΌμ§λ€.
ContactsContract.Contacts.CONTENT_URI : λͺ¨λ μ¬λ μΆλ ₯
ContactsContract.CommonDataKinds.Phone.CONTENT_URI : μ νλ²νΈκ° μλ μ¬λλ§ μΆλ ₯
ContactsContract.CommonDataKinds.Email.CONTENT_URI : μ΄λ©μΌ μ λ³΄κ° μλ μ¬λλ§ μΆλ ₯
val cursor = contentResolver.query(
it.data!!.data!!, // 첫 λ²μ§Έ λ§€κ°λ³μλ‘ μλ³μλ₯Ό μ λ¬
arrayOf<String>(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, // μ΄λ¦
ContactsContract.CommonKinds.Phone.NUMBER // μ νλ²νΈ
),
null,
null,
null,
)
μ£Όμλ‘μμ μ λ¬ν κ²°κ³Όλ URL λ¬Έμμ΄ ννμ΄λ©° URL μ 맨 λ§μ§λ§ λ¨μ΄(μμμλ 1144) κ° μ μ κ° μ νν μ¬λμ μλ³μ κ°
μλ³μλ₯Ό 쑰건μΌλ‘ μ£Όμλ‘ μ±μ 컨ν μΈ νλ‘λ°μ΄λ μ΄μ©
μ νν μ μ λ°μ΄ν°λ URL ννλ‘ λμ΄μ¨λ€. μλνλ©΄ μ μ κ° κ°μ§λ λ°μ΄ν°κ° λ무 λ§κΈ° λλ¬Έμ΄λ€. λ¨μν μ νλ²νΈλ§ μλ κ²μ΄ μλλΌ μ΄λ©μΌ, μ£Όμ, μ§ μ ν λ±μ μ 보λ μκΈ° λλ¬Έμ΄λ€. κ·Έλμ μ°λ¦¬ μ±μΌλ‘ λμμ€λ κ²μ μ νν μ μ μ μλ³μ
λ§ λμ΄μ€κ³ , μ΄κ²μ΄ URL ννμ΄λ€. κ·Έλ¦¬κ³ μ΄ μλ³μ
λ₯Ό μ΄μ©ν΄μ λ΄κ° μνλ λ°μ΄ν°λ₯Ό λ€μ μ£Όμλ‘μ 컨ν
νΈ νλ‘λ°μ΄λμ μμ²νλ€.
<uses-permission android:name="android.permission.READ_CONTACTS" />
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30dp"
android:textStyle="bold" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contacts App" />
</LinearLayout>
package com.kotdev99.android.c63
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
val resultView = findViewById<TextView>(R.id.resultView)
val requestActivity: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
// μ£Όμλ‘μμ λλμ μ€λ©΄ μ€ν λλ λΆλΆ
val cursor = contentResolver.query(
it.data!!.data!!, // ContentProvider λ° μ μ μ μλ³μ
arrayOf(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
),
null,
null,
null
)
var name = "none"
var phone = "none"
if (cursor!!.moveToFirst()) {
name = cursor?.getString(0).toString()
phone = cursor?.getString(1).toString()
}
resultView.text = "name - $name, phone - $phone"
}
val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
// νΌλ―Έμ
λ€μ΄μΌλ‘κ·Έ μ’
λ£ ν μ€ν λλ λΆλΆ
if (isGranted) {
val intent =
Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
requestActivity.launch(intent)
}
}
button.setOnClickListener {
val status =
ContextCompat.checkSelfPermission(this, "android.permission.READ_CONTACTS")
if (status == PackageManager.PERMISSION_GRANTED) {
// νΌλ―Έμ
νμ© μν μ λ°λ‘ μμμ μΈν
νΈ λ°μμμΌ μλ³μ νλ
val intent =
Intent(
Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI
)
requestActivity.launch(intent)
} else {
// νΌλ―Έμ
κ±°λΆ μν μ νΌλ―Έμ
μμ²
permissionLauncher.launch("android.permission.READ_CONTACTS")
}
}
}
}
κ°€λ¬λ¦¬λ μ£Όμλ‘κ³Ό λΉμ·νλ€.
μΈν νΈλ‘ κ°€λ¬λ¦¬ μ±μ μ¬μ§ λͺ©λ‘ μ‘ν°λΉν°λ₯Ό λμ°κ³ -> μ μ κ° μ νν μ¬μ§μ μλ³μ 'λ§' κ°μ Έμ€κ³ -> μλ³μ κ°μ μ΄μ©ν΄μ 컨ν νΈ νλ‘λ°μ΄λλ₯Ό μ°λνμ¬ λ°μ΄ν°λ₯Ό νλ
val intent = Intent(Intent.ACTION_PICK, MedialStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
requestActivity.launch(intent)
μμ¦ μΉ΄λ©λΌ μ±λ₯μ΄ λ무 λμμ Έ μΉ΄λ©λΌλ‘ μ°μ μ΄λ―Έμ§ μ¬μ΄μ¦κ° λ무 ν¬λ€.
μ΄λ¬λ©΄ OOM λ¬Έμ
κ° λ°μ ν μ μλ€. νλ©΄μ μΆλ ₯λλ μ¬μ΄μ¦κ° μλλΌ μμ λ°μ΄ν° μ¬μ΄μ¦λ₯Ό μ€μ¬μ λ‘λ©ν΄μΌ νλ€.
val option = BitmapFactory.Options()
option.inSampleSize = 5 // 1/5 λ‘ μ¬μ΄μ¦ λ€μ΄
OOM (OutOfMemoryException) λ¬Έμ
κ° λ°μ ν μ μλ μν©
BitmapFactory.Option κ°μ²΄μ inSampleSize κ°μ μ§μ ν΄ λ°μ΄ν° μ¬μ΄μ¦λ₯Ό μ€μ¬μ λ‘λ©
InputStream μ μ΄μ©ν΄μ κ°€λ¬λ¦¬ μ±μμ λ°μ΄ν°λ₯Ό νλνκ³ , νλν λ°μ΄ν°λ₯Ό BitmapFactory μ λ겨μ μ΄λ―Έμ§ κ°μ²΄λ₯Ό λ§λ λ€.
var inputStream = contentResolver.openInputStream(it.data!!.data)
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)
κ°€λ¬λ¦¬ μ±μ ContentProvider κ° μ 곡νλ InputStream κ°μ²΄λ₯Ό νλ
InputStream κ°μ²΄μ μν΄ λμ΄μ€λ λ°μ΄ν° μ΄μ©
openInputStream μ λ§€κ°λ³μλ‘ μλ³μλ₯Ό νλ‘λ°μ΄λμκ² μ λ¬νλ©΄μ λ°μ΄ν°λ₯Ό λ°μ μ μλ InputStream μμ²
BitmapFactory μκ² InputStream μ λ겨 InputStream μΌλ‘ λμ΄μ€λ λ°μ΄ν°λ‘ μ΄λ―Έμ§ κ°μ²΄ μμ±
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
package com.kotdev99.android.c64
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
val button = findViewById<Button>(R.id.button)
val launcher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
// κ°€λ¬λ¦¬ μ±μμ λλμ μμ λ μ€ν λλ μμ
try {
val option = BitmapFactory.Options()
option.inSampleSize = 5
// 컨ν
νΈ νλ‘λ°μ΄λμκ² μλ³μμ ν΄λΉ λλ λ°μ΄ν° InputStream μμ²
val inputStream = contentResolver.openInputStream(it.data!!.data!!)
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)
inputStream!!.close()
bitmap?.let {
imageView.setImageBitmap(bitmap)
} ?: let {
}
} catch (e: Exception) {
e.printStackTrace()
}
}
button.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
launcher.launch(intent)
}
}
}
μ νλ₯Ό κ±Έκ±°λ λ°λ μ±μ μ§μΉνλ€. λ무 μ ꡬνλ μ±μ΄λΌ μ ν κΈ°λ₯μ κ·Έλ₯ μ΄κ±° μ°λ©΄ λλ€.
1. νΌλ―Έμ
νμ
2. μΈν
νΈμ μ‘μ
λ¬Έμμ΄μ Intent.ACTION_CALL λ‘ μ§μ
3. data μ 보μ URL μ tel: μΌλ‘ μ μΈ (Call App μ μ‘ν°λΉν°κ° tel: νλ‘ν μ½ λͺ
μΌλ‘ λ°μ΄ν°λ₯Ό λ°κΈ° λλ¬Έ)
4. data μ λ³΄λ‘ μ ν λ²νΈ λͺ
μ
<uses-permission android:name="android.permission.CALL_PHONE" />
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/editView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="phone" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Call" />
</LinearLayout>
package com.kotdev99.android.c65
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editView = findViewById<EditText>(R.id.editView)
val button = findViewById<Button>(R.id.button)
val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:${editView.text}"))
startActivity(intent)
} else {
Toast.makeText(this, "denied", Toast.LENGTH_SHORT).show()
}
}
button.setOnClickListener {
val status = ContextCompat.checkSelfPermission(this, "android.permission.CALL_PHONE")
if (status == PackageManager.PERMISSION_GRANTED) {
val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:${editView.text}"))
startActivity(intent)
} else {
permissionLauncher.launch("android.permission.CALL_PHONE")
}
}
}
}
μΉ΄λ©λΌ μ±μ μ°λνμ¬ μ¬μ§μ 촬μνκ³ κ²°κ³Όλ₯Ό λλλ € λ°λ λ°©λ²μ λ κ°μ§ λ°©λ²μ΄ μλ€.
μ¬μ§ λ°μ΄ν° νλ λ°©λ²
νμΌ κ³΅μ λ°©λ²
// μ¬μ§ λ°μ΄ν° νλ λ°©λ²
1. μΉ΄λ©λΌ μ±μ μ¬μ§μ file λ‘ μ μ₯νμ§ μκ³ λ°μ΄ν°λ§ λκΈ΄λ€.
2. κ·Έλμ κ°€λ¬λ¦¬ λ±μ λ¨μ§ μλλ€.
3. κ·Έλ°λ° μ¬μ΄μ¦κ° μκ² λμ΄μ¨λ€. (OOM λ¬Έμ λλ¬ΈμΈ λ―?)
// νμΌ κ³΅μ λ°©λ²
1. μΈν
νΈλ₯Ό λ°μμν¬ λ μ±μ νμΌμ 보λ₯Ό κ°μ΄ λ겨μ€λ€.
2. μΉ΄λ©λΌ μ±μ΄ μ¬μ§μ 촬μνκ³ file λ‘ μ μ₯ν ν μ±κ³΅/μ€ν¨ μ¬λΆλ§ λ겨μ€λ€. (μ¬μ§μ ν λ°μ΄ν°λ‘ μ μ₯λλ€.)
3. μ°λ¦¬ μ±μ μ μ₯λ νμΌμ μ½μ΄ λ€μΈλ€.
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
requestActivity.launch(intent)
val bitmap = it.data?.getExtras()?.get("data") as Bitmap
λμ΄μ€λ λ°μ΄ν°λ₯Ό κ·Έλ₯ Bitmap κ°μ²΄λ‘ μ΄μ© ν΄μ£Όλ©΄ λλ€.
// νμΌ κ³΅μ λ°©λ²
1. μ±μμ μ¬μ§μ μ μ₯ν νμΌμ λ§λ λ€.
2. νμΌ μ 보λ₯Ό ν¬ν¨ν΄μ μΈν
νΈλ₯Ό λ°μμμΌ μΉ΄λ©λΌ μ±μ μ€ν μν¨λ€.
3. μΉ΄λ©λΌ μ±μμ μ¬μ§ 촬μ ν 촬μλ μ¬μ§μ 곡μ λ νμΌμ μ μ₯μ νλ€.
4. μΉ΄λ©λΌ μ±μ΄ μ’
λ£ λλ©΄μ μ±κ³΅ μ€ν¨λ₯Ό λ°ννλ€.
5. μ±μμ νμΌμ μ½μ΄ μΉ΄λ©λΌ μ±μ΄ μ μ₯ν μ¬μ§ λ°μ΄ν°λ₯Ό μ΄μ©νλ€.
κ·Έλ°λ° νμΌλ‘ μ΄μ©μ ν κ²½μ°μλ μ‘°κΈ μμ μ ν΄μΌ νλ€. νμΌμ 곡μ νκΈ° λλ¬Έμ λ°μνλ μμ λ€ μ΄λ€.
XML νμΌμ λ§λ€μ΄ μ€μΌ νλ€.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="myfiles" path="Android/data/com.kotdev99.android.c66/files/Pictures"/>
</paths>
FileProvider λ₯Ό μ΄μ©νλ €λ©΄ 곡μ νκ³ μ νλ νμΌμ Uri κ°μ μ€λΉ
path λ κ·Έλ₯ ν¨ν€μ§ λͺ μΌλ‘ μμ±νλ€.
κ·Έλ¦¬κ³ κ·Έ XML νμΌμ Manifest μ λ±λ‘ν΄ μ€μΌ νλ€.
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.kotdev99.android.c66.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
AndroidManifest.xml νμΌμ λ±λ‘
<provider>
νκ·Έλ‘ λ±λ‘νλ€.
νλ‘λ°μ΄λλ₯Ό λ§λλ κ²μ΄ μλλΌ λΌμ΄λΈλ¬λ¦¬μ μλ κ±Έ κ°μ Έλ€ μΈ λΏμ΄λ€.
μ°λ¦¬λ resource
μ XML νμΌ μ λ³΄λ§ μ§μ ν΄μ£Όλ©΄ λλ€.
val file = File.createTempFile(
"JPEG_${timeStamp}_",
".jpg",
storageDir
)
filePath = file.absolutePath
μμ±λ νμΌμ μλ³μλΌκ³ 보면 λλ€.
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.kotdev99.android.c66.fileprovider", file
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
fileRequestActivity.launch(intent)
νμΌ κ²½λ‘μμ λ°μ΄ν°λ₯Ό λ½μ μ΄λ―Έμ§ κ°μ²΄ νλνλ€.
val bitmap = BitmapFactory.decodeFile(filePath, option)
DATA λ²νΌμ μ΄λ―Έμ§ λ°μ΄ν°λ₯Ό νλνκ³ , FILE λ²νΌμ μ΄λ―Έμ§ νμΌμ νλνκ²λ ꡬν ν΄λ³΄μ!
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.cardview.widget.CardView
android:layout_width="150dp"
android:layout_height="150dp"
app:cardCornerRadius="75dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/dataButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="data" />
<Button
android:id="@+id/fileButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="file" />
</LinearLayout>
res/xml νμμ File XML μμ±
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="myfiles"
path="Android/data/com.kotdev99.android.c66/files/Pictures" />
</paths>
<provider>
νκ·Έμ <meta-data>
νκ·Έ λ±λ‘
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AndroidLab">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.kotdev99.android.c66.file-provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package com.kotdev99.android.c66
class MainActivity : AppCompatActivity() {
@SuppressLint("SimpleDateFormat")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
val dataButton = findViewById<Button>(R.id.dataButton)
val fileButton = findViewById<Button>(R.id.fileButton)
val launcher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
val bitmap = it.data?.extras?.get("data") as Bitmap
bitmap?.let {
imageView.setImageBitmap(bitmap)
}
}
dataButton.setOnClickListener {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
launcher.launch(intent)
}
var filePath = ""
val fileLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
val option = BitmapFactory.Options()
option.inSampleSize = 3
val bitmap = BitmapFactory.decodeFile(filePath, option)
bitmap?.let {
imageView.setImageBitmap(bitmap)
}
}
fileButton.setOnClickListener {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmm ss").format(Date())
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File.createTempFile(
"JPEG_${timeStamp}_", // νμΌλͺ
".jpg",
storageDir
)
filePath = file.absolutePath
val uri = FileProvider.getUriForFile(
this,
"com.kotdev99.android.c66.file-provider",
file
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
fileLauncher.launch(intent)
}
}
}