오늘은 앞선 콘텐츠 프로바이더에서 배운 내용을 직접 실습해보려 한다.
갤러리앱, 카메라 앱과 연동해서 앱의 프로필 사진을 교체하는 기능을 구현해보려 한다. 갤러리 앱의 사진 목록에서 사진을 선택하거나, 카메라로 촬영한 사진을 바로 프로필 사진으로 출력하는 앱이다.
메인 화면
갤러리 앱 버튼 클릭시 갤러리에서 사진 셀렉하면 바로 프로필 사진으로 등록됨
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<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.example.ch16_provider.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AndroidLab">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
lateinit var filePath: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
//gallery request launcher
val requestGalleryLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult())
{
try {
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.userImageView.setImageBitmap(bitmap)
} ?: let{
Log.d("event", "bitmap null")
}
}catch (e: Exception){
e.printStackTrace()
}
}
binding.galleryButton.setOnClickListener {
//gallery app........................
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
requestGalleryLauncher.launch(intent)
}
//camera request launcher.................
val requestCameraFileLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()){
val calRatio = calculateInSampleSize(
Uri.fromFile(File(filePath)),
resources.getDimensionPixelSize(R.dimen.imgSize),
resources.getDimensionPixelSize(R.dimen.imgSize)
)
val option = BitmapFactory.Options()
option.inSampleSize = calRatio
val bitmap = BitmapFactory.decodeFile(filePath, option)
bitmap?.let {
binding.userImageView.setImageBitmap(bitmap)
}
}
binding.cameraButton.setOnClickListener {
//camera app
//파일 준비
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 photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.ch16_provider.fileprovider",
file
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
requestCameraFileLauncher.launch(intent)
}
}
private fun calculateInSampleSize(fileUri: Uri, reqWidth: Int, reqHeight: Int): Int {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
try {
var inputStream = contentResolver.openInputStream(fileUri)
//inJustDecodeBounds 값을 true로 설정한 상태에서 decodeXXX() 를 호출
//로딩 하고자 하는 이미지의 각종 정보가 options 에 설정
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
}
}
<?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"
tools:context=".MainActivity"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프로필 사진 교체"
android:textStyle="bold"
android:layout_marginBottom="24dp"/>
<androidx.cardview.widget.CardView
android:layout_width="150dp"
android:layout_height="150dp"
app:cardCornerRadius="75dp"
app:cardElevation="0dp"
>
<ImageView
android:id="@+id/userImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/user_basic"
android:scaleType="centerCrop"
/>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/galleryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gallery App"
android:layout_marginTop="24dp"/>
<Button
android:id="@+id/cameraButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Camera App"
android:layout_marginTop="24dp"/>
</LinearLayout>
<?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.example.ch16_provider/files/Pictures"/>
</paths>