이 글은 유데미 강의 Android 12 및 Kotlin 개발 완전 정복을 참고하여 작성하였습니다.
작성자 : 정호영
개발환경은 Windows, Android Studio입니다.
126~131 강의 내용 정리입니다.
1. 권한 데모
1. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.tutorials.permissiondemo">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<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.PermissionDemo">
<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="eu.tutorials.permissiondemo">
<uses-permission
android:name="android.permission.CAMERA"/> 카메라 권한 요청
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/> 위치권한 요청
2. MainActivity.kt
package eu.tutorials.permissiondemo
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
private val cameraAndLocationResultLauncher:ActivityResultLauncher<Array<String>> =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
){permissions->
Log.d("MainActivity","Permissions $permissions")
permissions.entries.forEach {
val permissionName = it.key
val isGranted = it.value
if (isGranted) {
if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) {
Toast.makeText(
this,
"Permission granted for location",
Toast.LENGTH_LONG
)
.show()
}else{
Toast.makeText(
this,
"Permission granted for Camera",
Toast.LENGTH_LONG
)
.show()
}
} else {
if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) {
Toast.makeText(
this,
"Permission denied for location",
Toast.LENGTH_LONG
)
.show()
}else{
Toast.makeText(
this,
"Permission denied for Camera",
Toast.LENGTH_LONG
)
.show()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission)
btnCameraPermission.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA
)
) {
showRationaleDialog(" Permission Demo requires camera access",
"Camera cannot be used because Camera access is denied")
} else {
cameraAndLocationResultLauncher.launch(
arrayOf(Manifest.permission.CAMERA,Manifest.permission.ACCESS_FINE_LOCATION)
)
}
}
}
private fun showRationaleDialog(
title: String,
message: String,
) {
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder.setTitle(title)
.setMessage(message)
.setPositiveButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
builder.create().show()
}
}
2-1
private val cameraAndLocationResultLauncher:ActivityResultLauncher<Array<String>> =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
){permissions->
Log.d("MainActivity","Permissions $permissions")
permissions.entries.forEach {
val permissionName = it.key
val isGranted = it.value
forEach문을 통해 어떤 권한을 승인했는지 반복해서 체크하여 isGranted 에 담는다.
2-2
if (isGranted) {
if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) {
Toast.makeText(
this,
"Permission granted for location",
Toast.LENGTH_LONG
).show()
}
else{
Toast.makeText(
this,
"Permission granted for Camera",
Toast.LENGTH_LONG
).show()
}
}
만약 사용자로부터 권한요청이 수락된다면 각각의 요청에 대해서 Toast로 알림을 띄어준다.
else {
if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) {
Toast.makeText(
this,
"Permission denied for location",
Toast.LENGTH_LONG).show()
}
else{
Toast.makeText(
this,
"Permission denied for Camera",
Toast.LENGTH_LONG).show()
}
}
}
만약 사용자로부터 권한요청이 허용되지 않는다면 거부됬다는 알림을 띄운다.
private fun showRationaleDialog(title: String, message: String)
{
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder.setTitle(title)
.setMessage(message)
.setPositiveButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
builder.create().show()
}
사용자가 권한요청을 거부하였을 때 권한이 필요한 이유를 표시하는 대화 상자를 만들어서 표시해준다.
imageButton.setOnClickListener { view ->
Snackbar.make(view, "You have clicked image button.", Snackbar.LENGTH_LONG).show()
이미지버튼에 스낵바 다이얼로그를 추가한다.
Snackbar.make(뷰, 메시지, 알림시간)
스낵바는 경고, 주의 메시지에 적합하며 액티비티에서만 보인다는 특징이 있다.
2. AlertDialog, CustomDialog
[AlertDialog]
val btnAlertDialog: Button = findViewById(R.id.btn_alert_dialog)
btnAlertDialog.setOnClickListener { view ->
alertDialogFunction()
}
alert 메시지는 아이콘이 표시되며 사용자로부터 Yes, No 를 입력받을 수 있다는 특징이 있다.
private fun alertDialogFunction() {
val builder = AlertDialog.Builder(this)
builder.setTitle("Alert")
builder.setMessage("This is Alert Dialog. Which is used to show alerts in our app.")
builder.setIcon(android.R.drawable.ic_dialog_alert)
builder.setPositiveButton("Yes") { dialogInterface, which ->
Toast.makeText(applicationContext, "clicked yes", Toast.LENGTH_LONG).show()
dialogInterface.dismiss()
}
builder.setNeutralButton("Cancel") { dialogInterface, which ->
Toast.makeText(
applicationContext,
"clicked cancel\n operation cancel",
Toast.LENGTH_LONG
).show()
dialogInterface.dismiss()
}
builder.setNegativeButton("No") { dialogInterface, which ->
Toast.makeText(applicationContext, "clicked No", Toast.LENGTH_LONG).show()
dialogInterface.dismiss()
}
val alertDialog: AlertDialog = builder.create()
alertDialog.setCancelable(false)
alertDialog.show()
}
Alert.Builder를 사용하여 builder 속성(Title, Message, Icon, PositiveButton, NegativeButton 등)을 추가해준다.
Yes 버튼을 눌렀을 때 "clicked yes" 문구를 띄우고,
No 버튼을 눌렀을 때 "clicked no" 문구를 띄우며
종료버튼(cancle)을 눌렀을때 "clicked cancle opertaion cancel" 문구를 출력한다.
각 버튼에 대해 dissmiss()를 사용하여 AlertDialog를 종료한다.
[Custom Dialog]
private fun customDialogFunction() {
val customDialog = Dialog(this)
customDialog.setContentView(R.layout.dialog_custom)
customDialog.findViewById<TextView>(R.id.tv_submit).setOnClickListener {
Toast.makeText(applicationContext, "clicked submit", Toast.LENGTH_LONG).show()
customDialog.dismiss()
}
customDialog.findViewById<TextView>(R.id.tv_cancel).setOnClickListener {
Toast.makeText(applicationContext, "clicked cancel", Toast.LENGTH_LONG).show()
customDialog.dismiss()
}
customDialog.show()
}
layout 밑에 TextView 에서 id값을 추가하여 대응되는 dialog를 만들 수 있다.
tv_submit 을 누르게 되면 "clicked submit" 을 출력하고
tv_cancel 을 누르게 되면 "clicked cancle" 을 출력한다.
마찬가지로 dismiss() 를 통해 해당 다이얼로그를 종료한다.
<TextView
android:id="@+id/tv_submit"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:foreground="?attr/selectableItemBackground"
android:gravity="center"
android:padding="10dp"
android:text="SUBMIT"
android:textColor="@android:color/holo_red_dark"
/>
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:foreground="?attr/selectableItemBackground"
android:gravity="center"
android:padding="10dp"
android:text="CANCEL"
android:textColor="@android:color/black"
/>
val btnCustomProgress:Button = findViewById(R.id. btn_custom_progress_dialog)
btnCustomProgress.setOnClickListener {
customProgressDialogFunction()
}
btn_custom_progress_dialog에 대한 xml 파일
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<ProgressBar
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginEnd="10dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Please Wait..."
android:textColor="@android:color/black"
android:textSize="16sp"
/>
</LinearLayout>
customProgressDialogFunction() 함수
private fun customProgressDialogFunction() {
val customProgressDialog = Dialog(this)
customProgressDialog.setContentView(R.layout.dialog_custom_progress)
customProgressDialog.show()
}
3. 드로잉앱에 권한요청 추가
MainActivity.kt
val requestPermission: ActivityResultLauncher<Array<String>> =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
{ permissions ->
permissions.entries.forEach {
val perMissionName = it.key
val isGranted = it.value
if (isGranted ) {
Toast.makeText(
this@MainActivity,
"Permission granted now you can read the storage files.",
Toast.LENGTH_LONG).show()
}
else {
if (perMissionName == Manifest.permission.READ_EXTERNAL_STORAGE)
Toast.makeText(
this@MainActivity,
"Oops you just denied the permission.",
Toast.LENGTH_LONG
).show()
}
}
}
126번 강의에서 했었던 내용과 동일하다. 사용자로부터 권한요청이 승인되면 Toast를 통해 문구를 출력한다.
private fun requestStoragePermission(){
if (
ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.READ_EXTERNAL_STORAGE)
)
{
showRationaleDialog("Kids Drawing App","Kids Drawing App " +
"needs to Access Your External Storage")
}
else {
requestPermission.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
)
}
}
사용자에게 이 기능을 왜 액세스 해야되는지에 대해 showRationalDialog() 함수를 호출하여 알려준다.
만약 권한요청을 확인하지 못했다면 else 문에서 다시한번 권한을 요청한다.
arrayOf 안에 필요한 권한들을 계속 추가해줄 수 있다.
4. 갤러리에서 이미지 받아오기
val pickIntent = Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
isGranted 조건문안에 pickIntent 변수를 만들어 미디어 스토어로부터 값을 받아오고 이미지를 선택하기 위해 런처를 만든다.
val openGalleryLauncher:ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{result->
if (result.resultCode == RESULT_OK && result.data != null){
val imageBackground:ImageView = findViewById(R.id.iv_background)
imageBackground.setImageURI(result.data?.data)
}
}
result 에 데이터가 비어있는지를 확인하고 result에 있는 데이터를 imageBackground 이미지뷰 변수에 추가한다.
<ImageView
android:id="@+id/iv_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="image"
android:scaleType="centerCrop" />
val pickIntent = Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
openGalleryLauncher.launch(pickIntent)
런처를 통해 Intent를 개시해 엑세스할 수 있게 한다.
'GDSC HUFS 4기 > Kotlin Team #3' 카테고리의 다른 글
[3팀] Android-12-Kotlin : 키즈 드로잉 앱 (4) (0) | 2022.11.21 |
---|---|
[3팀] Android 12 : 키즈 드로잉 앱 (2) (0) | 2022.11.16 |
[3팀] 멤버변수, 세터/게터, 데이터 클래스, 상속 (0) | 2022.11.16 |
[3팀] Android-12-Kotlin : 키즈 드로잉 앱 (1) (0) | 2022.11.15 |
[3팀] Android-12-Kotlin : 퀴즈 앱 안드로이드 (0) | 2022.11.11 |