본문 바로가기

카테고리 없음

[Android Kotiln] 코틀린 사진 권한획득, 불러오기, 추가 - SAF (Storage Access Framework)

반응형

 

간단하게 코틀린을 이용하여 사진을 추가해보자

 

[activity_main.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
 
    <ImageView
        android:id="@+id/imageView"
        android:layout_marginTop="100dp"
        android:layout_width="0dp"
        android:layout_height="500dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />
 
    <Button
        android:id="@+id/addPhotoButton"
        android:text="사진 추가하기"
        android:textSize="20sp"
        android:layout_width="200dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/imageView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="10dp"
        />
 
 
 
</androidx.constraintlayout.widget.ConstraintLayout>
cs

해당 화면을 간단하게 만들어보았다

 

 

 

1
2
3
4
5
6
7
    val addPhotoButton: Button by lazy {
        findViewById<Button>(R.id.addPhotoButton)
    }
 
    val showImage: ImageView by lazy{
        findViewById<ImageView>(R.id.imageView)
    }
cs

먼저, Button과 ImageView를 findViewById를 이용해 연결해준다

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        addPhoto()
    }
 
    private fun addPhoto() {
        addPhotoButton.setOnClickListener {
            when {
                ContextCompat.checkSelfPermission(
                    this,
                    android.Manifest.permission.READ_EXTERNAL_STORAGE
                ) == PackageManager.PERMISSION_GRANTED -> {
                    //권한이 부여되었을 경우
                    takePhotos()
 
                }
 
                shouldShowRequestPermissionRationale(android.Manifest.permission.READ_EXTERNAL_STORAGE)
                ->{
                    //교육용 팝업 띄우기
                    showPermissionContextPopup()
                }
                else -> {
                    requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1000)
                }
            }
        }
    }
cs

다음으로는 사진추가버튼을 클릭했을 경우 권한을 획득되있는지 확인하는 작업을 해준다

 

이미 권한이 부여되어있을 경우에는 takePhotos()로 넘어가도록 하고

 

권한을 거부 했을 경우에는 shouldShowRequestPermissionRationale를 이용해 권한이 필요하다는 dialog를 띄워준다

 

그리고 else인 경우에는 android.Manifest에 권한을 부여시켜주게 된다

 

여기서 requestPermissions에 마지막에 위치한 1000이란 코드를 잘 보아야 한다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
 
        when (requestCode) {
            1000 -> {
                if (grantResults.isNotEmpty() && grantResults[0== PackageManager.PERMISSION_GRANTED) {
                    //권한이 부여된 경우임
                    takePhotos()
 
                } else {
                    Toast.makeText(this"권한을 거부하였습니다", Toast.LENGTH_SHORT).show()
                }
            }
            else -> {
                //
            }
        }
    }
cs

해당 코드는 requestCode로 권한이 확인됬다는 확인코드로 작동된다

 

따라서 when에서 requestCode가 1000인 것을 확인 했다면 권한이 부여된 것을 확인하고 takePhotos()로 이동한다

 

다른 requestCode를 받았다면 권한이 거부된 것으로 처리한다

 

DENY를 눌렀을 경우 뜨는 Dialog

 

1
2
3
4
5
6
7
8
    private fun takePhotos(){
        //SAF 기능 활성화
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        //이미지만 가져오도록 설정
        intent.type = "image/*"
 
        startActivityForResult(intent, 2000)
    }
cs

takePhotos()에는 SAF (Storage Access Framework)를 실행하게 해준다

 

이 기능은 기기 Storage에서 파일을 가져오게 해주는 기능이다

 

지금은 사진만 가져오기 위해 intent.type = "image/*" 로 설정해 주었다

 

여기서도 2000으로 된 requestCode를 기억해두어야한다

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
 
        if (resultCode != Activity.RESULT_OK){
            //Activity가 정상적으로 실행되지 않았을경우 예외처리
            return
        }
 
        when(requestCode){
            2000 -> {
                val selectedImageUri: Uri? = data?.data
                if (selectedImageUri != null){
                    //사진을 가져왔을 경우
                    showImage.setImageURI(selectedImageUri)
                } else {
                    //사진 불러오기 실패
                    Toast.makeText(this"사진을 가져오지 못했습니다", Toast.LENGTH_SHORT).show()
                }
            }
            else -> {
                Toast.makeText(this"사진을 가져오지 못했습니다", Toast.LENGTH_SHORT).show()
            }
        }
    }
cs

onActivityResult에서도 마찬가지로 SAF에서 intent로 가져온 requestCode를 사용하기 때문에

 

2000일 경우에 ImageUri로 사진을 Imageview에 이미지를 불러오도록 할 수 있게 된다

 

 

 

1
2
3
4
5
6
7
8
9
10
11
    private fun showPermissionContextPopup(){
        AlertDialog.Builder(this)
            .setTitle("권한이 필요합니다")
            .setMessage("사진을 불러오기 위한 권한이 필요합니다")
            .setPositiveButton("동의"){ _ , _ ->
                requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1000)
            }
            .setNegativeButton("취소"){ _ , _ -> }
            .create()
            .show()
    }
cs

마지막은 교육용 팝업을 띄워주는 showPermissionContextPopup()이다

 

사용자가 해당 앱의 기능을 거부하였을 경우 다시한번 권한을 동의하게 만들게 유도하는 창이다

 

dialog를 이용해서 동의와 취소를 구현해준다

 

 

 

 

 

 

[MainActivity 전체코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package fastcampus.aop.part2.test_image
 
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import kotlinx.coroutines.selects.select
 
class MainActivity : AppCompatActivity() {
 
    val addPhotoButton: Button by lazy {
        findViewById<Button>(R.id.addPhotoButton)
    }
 
    val showImage: ImageView by lazy{
        findViewById<ImageView>(R.id.imageView)
    }
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        addPhoto()
    }
 
    private fun addPhoto() {
        addPhotoButton.setOnClickListener {
            when {
                ContextCompat.checkSelfPermission(
                    this,
                    android.Manifest.permission.READ_EXTERNAL_STORAGE
                ) == PackageManager.PERMISSION_GRANTED -> {
                    //권한이 부여되었을 경우
                    takePhotos()
 
                }
 
                shouldShowRequestPermissionRationale(android.Manifest.permission.READ_EXTERNAL_STORAGE)
                ->{
                    //교육용 팝업 띄우기
                    showPermissionContextPopup()
                }
                else -> {
                    requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1000)
                }
            }
        }
    }
 
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
 
        when (requestCode) {
            1000 -> {
                if (grantResults.isNotEmpty() && grantResults[0== PackageManager.PERMISSION_GRANTED) {
                    //권한이 부여된 경우임
                    takePhotos()
 
                } else {
                    Toast.makeText(this"권한을 거부하였습니다", Toast.LENGTH_SHORT).show()
                }
            }
            else -> {
                //
            }
        }
    }
 
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
 
        if (resultCode != Activity.RESULT_OK){
            //Activity가 정상적으로 실행되지 않았을경우 예외처리
            return
        }
 
        when(requestCode){
            2000 -> {
                val selectedImageUri: Uri? = data?.data
                if (selectedImageUri != null){
                    //사진을 가져왔을 경우
                    showImage.setImageURI(selectedImageUri)
                } else {
                    //사진 불러오기 실패
                    Toast.makeText(this"사진을 가져오지 못했습니다", Toast.LENGTH_SHORT).show()
                }
            }
            else -> {
                Toast.makeText(this"사진을 가져오지 못했습니다", Toast.LENGTH_SHORT).show()
            }
        }
    }
 
    private fun showPermissionContextPopup(){
        AlertDialog.Builder(this)
            .setTitle("권한이 필요합니다")
            .setMessage("사진을 불러오기 위한 권한이 필요합니다")
            .setPositiveButton("동의"){ _ , _ ->
                requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1000)
            }
            .setNegativeButton("취소"){ _ , _ -> }
            .create()
            .show()
    }
 
    private fun takePhotos(){
        //SAF 기능 활성화
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        //이미지만 가져오도록 설정
        intent.type = "image/*"
 
        startActivityForResult(intent, 2000)
    }
}
cs

반응형