본문 바로가기
깡샘 코틀린

16-2 안드로이드 기본 앱과 연동하기

by 농농씨 2023. 7. 9.

이번에는 안드로이드의 기본 앱을 연동하는 방법을 알아보자. 안드로이드 기본 앱 가운데 주소록 갤러리, 카메라, 지도, 전화 앱을 내가 만든 앱과 어떻게 연동하는지 살펴보자.

 

 

주소록 앱 연동하기

주소록은 가장 많이 이용하는 안드로이드의 기본 앱이다. 핸드폰의 주소록 앱을 연동하여 주소록 목록을 띄우고 사용자가 선택한 사람의 전화번호나 이메일 정보를 가져와 내가 만든 앱에서 이용할 수 있다.

 

우선 주소록 앱에서 데이터를 가져오려면 다음과 같은 퍼미션을 설정해야 한다.

// 주소록 앱 사용 퍼미션 설정
<uses-permission android:name="android.permission.READ_CONTACTS" />

그리고 주소록의 목록 화면을 띄우는 코드를 작성한다.

// 주소록 목록 출력
val intent = intent(Intent.ACITON_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
// 인텐트로 액션문자열(어떤 앱을 열지?)과 데이터 정보(그 앱에서 어떤 데이터를 가져올지?) 설정함
requestContactsLauncher.launch(intent) // 시스템에 인텐트 전달함

주소록 목록 화면은 인텐트를 이용해 실행하는데 이때 액션 문자열은 intent.ACITON_PICK으로 하고 데이터는 ContactsContract.CommmonDataKinds.Phone.CONTENT_URI로 설정한다. 데이터 정보에 설정한 Uri 객체는 주소록 앱과 연동할 때 이용하는 상수이다. Uri 객체는 Uri.parse() 함수로 직접 지정해도 되지만 다음과 같은 상수를 이용해도 된다.

  • ContactsContract.Contacts.CONTENT_URI: 모든 사람의 데이터
  • ContactsContract.CommonDataKinds.Phone.CONTENT_URI: 전화번호가 있는 사람
  • ContactsContract.CommonDataKinds.Email.CONTENT_URI: 이메일 정보가 있는 사람

이렇게 정의한 인텐트를 ActivityResultLauncher의 launch() 함수로 시스템에 전달하면 주소록의 목록 화면이 출력되고, 사용자가 이 목록에서 한 사람을 선택하여 되돌아오면 ActivityResultCallback의 onActivityResult() 함수가 자동으로 실행된다.

// 주소록에서 사용자가 한 사람을 선택했을 때 실행되는 함수
requestContactslauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult())
{
    if (it.resultCode == RESULT_OK) {
        Log.d("kkang", "${it.data?.data}")
    }
}

onActivityResult() 함수의 매개변수로 주소록 앱에서 전달한 결과 데이터를 받을 수 있다. 이 데이터를 출력해 보면 다음처럼 주소록 앱이 전달한 문자열을 확인할 수 있다.

content://com.android.contacts/contacts/lookup/0r92-숫자~나열~/1144

주소록에서 전달한 데이터는 URL 문자열 형태이며 URL의 맨 마지막 단어(예에서는 1144)는 사용자가 선택한 사람의 식별값이다. 만약 사용자가 선택한 사람의 이름, 전화번호 등을 가져와야 한다면 위의 식별값을 조건으로 주소록 앱에 필요한 데이터를 구체적으로 다시 요청해야 한다. 이때 주소록 앱의 콘텐츠 프로바이더를 이용한다.

// 콘텐츠 프로바이더로 필요한 (구체적인) 데이터 요청
requestContactsLauncher = registerForactivityResult(
    ActivityResultContracts.StartActivityforResult())
{
    if (it.resultCode == RESULT_OK) {
        val cursor = contentResolver.query(
            it!!.data!!.data!!,
            arrayOf<String>(
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, // 이름 데이터 가져오기
                Contactsontract.CommonDataKinds.Phone.NUMBER // 전화번호 데이터 가져오기
            ),
            null,
            null,
            null
        )
        Log.d("kkang", "cursor size....${cusor?.count}")
        if (cursor!!,moveToFirst()) {
            val name = cursor?.getString(0)
            val phone = cursor?.getString(1)
            binding.resultContact.text = "name: $name, phone: $phone"
        }
    }
}

주소록 앱의 콘텐츠 프로바이더를 이용할 때 가져올 데이터를 ContactsContract.commonDataKinds.Phone.DISPlAY_NAME과 ContactsContract.CommonDataKinds.Phone.NUMBER로 지정했다. 이 두 코드는 각각 이름과 전화번호를 의미한다.

 

 

갤러리 앱 연동하기

갤러리 앱을 연동해 사진 이미지를 가져오는 방법도 살펴보자. 갤러리 앱 연동은 인텐트로 갤러리 앱의 목록 화면을 띄우거나 갤러리 앱의 콘텐츠 프로바이더로 데이터를 가져오는 작업이다.

 

이미지 작업 시 고려 사항

먼저 안드로이드 앱에서 이미지 데이터를 이용할 때 고려해야 할 사항은 다음과 같다.

  • 안드로이드에서 이미지는 Drawable이나 Bitmap 객체로 표현한다.
  • Bitmap 객체는 BitmapFactory로 생성한다.
  • BitmapFactory로 이미지를 생성할 때는 OOM 오류를 고려해야 한다.
  • Glide나 Picasso 같은 이미지 처리 라이브러리를 이용하는 것이 효율적일 수 있다.

안드로이드에서 이미지는 Drawable이나 Bitmap 객체로 표현한다. Drawable은 주로 리소스 이미지를 표현할 때, Bitmap파일에서 읽은 이미지나 네트워크에서 내려받은 이미지를 표현할 때 사용한다. Bitmap과 Drawable은 서로 호환하므로 Drawable 타입의 이미지를 Bitmap 타입으로, 또는 그 반대로 바꿀 수 있다.

 

Bitmap 이미지는 BitmapFactory 클래스의 'decode'로 시작하는 다음과 같은 함수로 생성한다.

  • BitmapFactory.decodeByteArray(): byte[] 배열이 데이터로 비트맵 생성
  • BitmapFactory.decodeFile(): 파일 경로를 매개변수로 지정하면 그 파일에서 데이터를 읽을 수 있는 FileInputStream을 만들어 decodeStream() 함수 이용
  • BitmapFactory.decodeResource(): 리소스 이미지로 비트맵 생성
  • BitmapFactory.decodeStream(): InputStream으로 읽은 데이터로 비트맵 생성

BitmapFactory를 이용하면 비트맵을 만들 때 아이콘처럼 작은 이미지를 불러오는 데는 문제가 없지만 카메라로 찍은 사진이나 서버에서 내려받은 이미지처럼 크기가 큰 이미지를 불러올 때는 OOM(out of memory) 오류가 발생할 수 있다. OOM이란 앱의 메모리가 부족해서 발생하는 오류이다. 이 오류는 주로 용량이 큰 이미지를 불러올 때 발생하므로 이미지의 크기를 줄이면 해결된다.

// 옵션을 지정하지 않고 비트맵 생성
val bitmap = BitmapFactory.decodeStream(inputStrea)

이처럼 decodeStream() 함수로 Bitmap 객체를 만들 때 매개변수에 옵션을 지정하지 않으면 원본 데이터 그대로 불러오므로 OOM 오류가 발생할 수 있다. 따라서 다음처럼 옵션을 적용한다. 이미지 크기를 줄일 때는 BitmapFactory.Option 객체의 inSampleSize 속성을 이용한다.

// 옵션을 지정해 비트맵 생성
val option = BitmapFactory.Options() // BitmapFactory의 Options 객체 생성
option.inSampleSize = 4 // Options 객체의 inSampleSize 속성에 값 적용, 이 값만큼의 비율로 데이터 줄여서 불러옴
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)

Option 객체의 inSampleSize에 값을 적용하면 이 값만큼의 비율로 데이터를 줄여서 불러온다. 예를 들어 inSampleSize값을 4로 지정하면 해상도가 2048×1536(약 12MB)인 이미지를 약 512×384(약 0.75MB)의 비트맵으로 생성해 준다.

 

갤러리 앱 연동 방법

이제 갤러리 앱을 연동하는 방법을 살펴보자. 인텐트로 갤러리 앱의 사진 목록을 띄우고 사용자가 선택한 사진을 읽어 화면에 출력하는 방법이다.

먼저 인텐트로 갤러리 앱의 사진 목록을 출력하는 코드를 다음과 같이 작성한다. 인텐트의 액션 문자열은 Intent.ACTION_PICK으로, 데이터는 MediaStore.Images.Media.EXTERNAL_CONTENT_URI로, 그리고 타입을 image/*로 지정한 인텐트를 전달하면 갤러리 앱의 목록 화면이 실행된다.

// 사진 목록 출력
val intent = Intent(Intent.ACITON_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
// 인텐트의 액션 문자열과 데이터 지정함
intent.type = "image/*" // 인텐트의 타입 지정함
requestGalleryLauncher.launch(intent) // 시스템에 인텐트 전달하여 갤러리 앱의 목록 화면 실행되게 함

 

다음으로 갤러리 앱의 사진 목록에서 사용자가 선택한 사진을 액티비티 화면에 출력할 차례이다. 이떄 앞에서 살펴본 OOM 문제가 발생할 수도 있으므로 BitmapFactory.Option 객체의 inSampleSize값을 지정해 이미지 크기를 줄여서 불러와야 한다. 그런데 inSampleSize값을 임의의 숫자로 지정할 수도 있지만 원본 데이터의 크기와 화면에 출력되는 이미지의 크기를 비교해서 적절한 비율로 지정하면 더 효율적이다.

 

다음 코드에서 작성한 함수는 inSampleSize에 지정할 값을 계산하는 예를 보여준다. 이 함수를 호출하면서 사진의 Uri값화면에 이미지가 출력되는 크기를 매개변수로 지정하면, 해당 이미지 정보를 얻어 실제 화면에 출력되는 크기와 비교해서 inSampleSize값을 계산한다*.

*비율을 계산하는로직은 필자가 임의로 작성했으므로, 더 효율적인 알고리즘이 있다면 다르게 작성해도 된다.

// 적절한 비율로 이미지 크기 줄이기
private fun calulateInSampleSize(fileUri: Uri reqWidth: Int, reqHeight: Int): Int {
				// 사진 Uri 값, 출력되는 크기를 매개변수로 함.
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true // 옵션만 설정하고자 true로 지정한다.
    try {
         var inputStream = contentResolver.openInputStream(fileUri)
         BitmapFactory.decodeStream(inputStream, null, options)
         // 각종 이미지 정보가 옵션에 설정된다.
         // decoeeStream() 함수로 이미지를 메모리에 불러오는 것처럼 보이지만, 세번째 매개변수에 지정한 options 객체의 
         // inJustDecodeBounds 값을 true로 설정했으므로 실제로 Bitmap 객체가 만들어지지 않는다.
         // 그 대신 읽은 이미지의 각종 정보가 option 객체에 설정된다.
         inputStream!!.close()
         inputStream = null
    } catch (e: Exception) {
        e.printStackTrace()
    }
    val (height: Int width: Int) = optinos.run { outHeight to outWidth }
    var inSamplesize = 1
    // inSampleSize 비율 계산
    if (height > reqHeight || width > reqWidth) { // if 문으로 계산한 부분 주목
        val halfHeight: Int = height / 2
        val halfWidth: Int = width / 2
        while (halfHeight / inSampleSize >= reqHeight && // 출력할 크기보다 이미지 크기가 크면
               halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2 // 더 큰숫자로 나누도록 한다(inSampleSize 크기가 커진다->무슨의미?)
        }
    }
    return inSampleSize
}

위 코드에서는 decodeStream() 함수로 이미지를 메모리에 불러오는 것처럼 보이지만, 세 번째 매개변수에 지정한 Option 객체의 inJustDecodeBounds 값을 true로 설정했으므로 실제로는 Bitmap 객체가 만들어지지 않는다. 그 대신 읽은 이미지의 각종 정보가 option 객체에 설정된다.

 

이제 갤러리 앱의 목록에서 사용자가 사진을 하나 선택해서 되돌아왔을 때 위의 calculateInSampleSize() 함수를 이용해 이미지를 불러오는 코드를 살펴보자.

// 이미지를 불러오는 코드
requestGalleryLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult())
{
    try {
        // inSampleSize 비율 계산, 지정
        val calRatio = calculateInSampleSize(it!!.data!!.data!!,
            resources.getDimensionPixelSize(R.dimen.imgSize), // 출력할 이미지의 화면 크기를 나타냄.
            resources.getDimensionPixelSize(R.dimen.imgSize)) // dimen 리소스값을 얻어 지정함. 임의의 숫자로 지정 가능.
        val option = BitmapFactory.Options()
        option.inSampleSize=calRatio
        
        // 이미지 로딩
        var inputStream = contentResolver.openInputStream(it!!.data!!.data!!)
        // 갤러리 앱의 프로바이더가 제공하는 InputStream 객체를 가져옴. 사용자가 선택한 사진 담겨있음.
        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()
    }
}

resources.getDimensionPixelSize(R.dimen.imgSize) 구문은 출력할 이미지의 화면 크기를 나타내며 dimen 리소스값을 얻어 지정했다. 임의의 숫자를 직접 지정해도 된다. 그리고 contentResolver.openInputStream(data!!.data!!) 구문은 갤러리 앱의 콘텐츠 프로바이더가 제공하는 InputStream 객체를 가져온다. 이 객체에는 사용자가 갤러리 앱에서 선택한 사진 데이터가 담겨 있다.

 

 

카메라 앱 연동하기

이번에는 앱에서 카메라 앱을 실행해 사진을 촬영하고 사진 데이터를 가져오는 방법을 살펴보자. 카메라 앱을 연동하여 사진을 촬영하고 그 결과를 돌려받는 방법은 다음 2가지이다.

  1. 사진 데이터를 가져오는 방법
  2. 사진 파일을 공유하는 방법

먼저 사진 데이터를 가져오는 방법은 카메라 앱으로 사진을 촬영한 후 파일로 저장하지 않고 데이터만 넘겨주는 방식이다. 이 방식은 사진을 파일로 저장하지 않으므로 👍쉽게 구현할 수 있지만 👎넘어오는 사진 데이터의 크기가 작다는 단점이 있다.

사진 파일을 공유하는 방법은 카메라 앱에서 촬영한 사진을 파일에 저장한 후 성공인지 실패인지만 넘겨주는 방식이다. 이 방식을 이용하면 휴대폰의 카메라 성능만큼 👍큰 크기의 사진을 촬영하고 앱에서 이용할 수 있지만, 카메라 앱이 파일 정보를 공유하는 것이므로 👎몇 가지 준비 작업을 해야 한다.

 

사진 데이터를 가져오는 방법

인텐트로 카메라 앱의 사진 촬영 액티비티를 실행한다.

// 사진 촬영 액티비티 실행
val intent = Intent(MediaStore.ACITON_IMAGE_CAPTURE) // 액션 문자열만 지정해서
requestCameraThumbnailLauncher.launch(intent) // 이 인텐트를 시스템에 전달해 카메라 실행되도록 함

인텐트에 액션 문자열을 MediaStore.ACTION_IMAGE_CAPTURE로 지정하여 시스템에 전달하면 카메라 앱이 실행된다. 카메라 앱에서 사용자가 사진을 촬영하고 <확인>을 누르면 다시 내가 만든 앱으로 되돌아온다. 그러면 카메라 앱에서 넘어온 사진 데이터ActivityResultCallback에서 다음과 같은 코드로 가져오면 된다.

// 사진 데이터 가져오기
requestCameraThumbnailLauncher = registerForactivityResult(
    ActivityResultContracts.StartActivityForResult())
{
    val bitmap = it?.data?.extras?.get("data") as Bitmap
}

(글쿤~ 하지만 이 코드에서는 ActivityResultCallback 이라는 함수는 보이지 않는걸~ 뭘까~)

 

사진 파일을 공유하는 방법

카메라 앱으로 촬영한 사진 파일을 공유하는 방법은 다음 절차를 따른다.

  1. 앱에서 사진을 저장할 파일을 만든다.
  2. 사진 파일 정보를 포함한 인텐트를 전달해 카메라 앱을 실행한다.
  3. 카메라 앱으로 사진을 촬영하여 공유된 파일에 저장한다.
  4. 카메라 앱을 종료하면서 성공 또는 실패를 반환한다.
  5. 카메라 앱이 저장한 사진 파일을 앱에서 이용한다.

사진 파일을 공유하는 방법을 이용하려면 먼저 앱에서 외장 메모리에 파일을 만들어 줘야 한다. 그런데 파일을 만들 때 getExternalStoragePublicDirectory() 또는 getExternalFileDir() 함수를 이용할 수도 있다. 전자는 모든 앱에서 이용할 수 있는 파일을 만든다면, 후자는 이 앱에서만 이용할 수 있는 파일을 만든다.

getExternalStoragePublicDirectory() 함수를 이용해 외장 메모리에 파일을 만들려면 다음과 같은 퍼미션을 설정해 줘야 한다. 원래는 getExternalFilesDir() 함수로 파일을 만들 때에도 다음과 같은 퍼미션을 설정해야 했지만 API 레벨 19버전부터는 이 작업을 할 필요가 없어졌다. 물론 API 레벨 19 하위 버전에서도 동작하는 앱을 만들려면 퍼미션을 설정해줘야 한다.

// 외장 메모리 사용 퍼미션
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

그런데 API 레벨 24 버전부터는 file:// 프로토콜로 구성된 URI를 외부에 노출하지 못하도록 하는 엄격 모드(strict mode)가 적용되었다. 따라서 앱끼리 파일을 공유하려면 1️⃣content:// 프로토콜을 이용하고 2️⃣이 URI에 임시로 접근할 수 있는 권한을 부여해야 한다. 이때 File Provider 클래스를 이용한다. FileProvider 클래스는 androidx 라이브러리에서 제공하며 XML 설정을 기반으로 해서 content:// 프로토콜로 구성된 URI를 생성해 준다.

결국 FileProvider를 이용하려면 공유할 파일의 URI값을 만들어야 한다는 의미이다. 그러려면 프로젝트의 res/xml 디렉터리에 파일 프로바이더용 XML 파일을 만들어 줘야 한다.

// 파일 프로바이더용 XML 파일
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="myfiles" path="Android/data/com.example.test16/files/Pictures" />
    // com.example.test16 은 앱의 패키지명을 의미함
</paths>

파일 프로바이더용 XML 파일은 path 속성지정한 경로의 파일 권한을 설정한다. 이곳에 지정한 경로는 getExternalFilesDir() 함수로 파일을 만들었을 때 파일이 저장되는 위치이다.

이렇게 작성한 파일 프로바이더용 XML 파일을 매니페스트 파일에 다음처럼 등록한다.

// 매니페스트에 파일 프로바이더용 XML 파일 등록
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.test16.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"></meta-data>
</provider>

매니페스트에 androidx 라이브러리에서 제공하는 프로바이더를 등록했다. <provider> 태그 아래에 <meta-data> 태그를 작성하고 resource 속성으로 res/xml에 만들어 놓은 XML 파일을 지정한다.(...글쿤)

 

이제 코드에서 카메라 앱을 연동해 볼 것이다. 우선 다음과 같이 파일을 하나 만들어 준다.

// 파일 만들기
val timeStamp: String = SimpleDateFormat("yyyyMMdd)HHmmss").format(Date())
 						// 파일명 중복되지 않도록 날짜와 시각 이용함
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
			// getExternalFilesDir() 로 파일만들면서 매개변수에 파일저장위치 지정함
val file = File.createTempFile(
    "JPEG_${timeStamp}_",
    ".jpg"
    storageDir
)
filePath = file.absolutePath // 파일 읽을 때 사용하려고 파일 경로 저장함.

getExternalFilesDir() 함수로 파일을 만들면서 매개변수에 Environment.DIRECTORY_PICTURES라고 지정했으므로 Android/data/com.example.test16/files/Pictures에 저장된다. 파일명이 중복되지 않도록 날짜와 시각을 이용했으며 나중에 파일 내용을 읽을 때 사용하려고 파일 경로를 filePath에 저장했다.

 

// 카메라 앱을 실행하는 인텐트
val photoURI: Uri = FileProvider.getUriorFile(
// 앞에서 설정한 정보를 바탕으로 FileProvider를 이용해 Uri 객체를 만들고
    this,
    "com.example.test16.fileprovider", file
)
val intent = intent(MediaStore.ACTION_IMAGE_CAPTURE) // 이를 카메라 앱을 실행하는 인텐트의 엑스트라 데이터로 설정함
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
requestCamraFileLauncher.launch(intent) // 인텐트로 카메라 실행 요청함

앞에서 설정한 정보를 바탕으로 FileProvider를 이용해 Uri 객체를 만드록 이를 카메라 앱을 실행하는 인텐트의 엑스트라 데이터로 설정했다. 이렇게 하면 파일 정보를 가지고 카메라 앱의 액티비티가 실행된다.

 

다음은 카메라 앱에서 사진을 촬영한 후 ㅕ 실행되는 코드이다.

// 비트맵 이미지 생성
requestCameraFileLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult())
{
    val option=BitmapFactory.Options()
    option.inSampleSize = 10 // 이미지 크기 줄이기(OOM 오류 막기 위해)
    val bitmap = BitmapFacory.decodeFile(filePath, option)
    // 파일 경로를 전달해서 비트맵 객체 얻음
    bitmap?.let {
        binding.cameraFileResult.setImageBitmap(bitmap)
    }
}

앞에서 저장해 뒀던 파일 경로를 decodeFile() 함수에 전달해서 비트맵 객체를 얻는다. 그리고 OOM 오류를 막으려고 BitmapFactory.Options의 inSampleSize값을 지정했다. 여기서는 단순히 10으로 고정했지만, 필요에 따라 아에서 살펴본 내용처럼 이미지 크기를 계산해서 적절한 비율로 지정할 수도 있다.

 

 

지도 앱 연동하기

앱이 위도와 경도의 값을 가지고 있다면 지도 앱을 연동해 위치를 보여 줄 수 있다. 지도 앱을 연동할 때는 인텐트의 액션 문자열을 Intent.ACTION_VIEW로 지정한다.

// 지도 앱을 실행하는 인텐트
val intent =
    Intent(Intent.ACITON_VIEW, Uri.parse("geo: // URL을 geo:로 시작하도록 하고
    37.5662952, 126.9779451")) // 위도와 경도를 쉼표로 구분해서 작성
startActivity(intent)

지도 가운데 특정 위치를 보이게 하려면 위도와 경도를 넘겨줘야 하는데 이 정보를 인텐트의 데이터 정보로 전달한다. 이때 URL은 반드시 geo:로 시작해야 하고 이어서 위도와 경도를 쉼표로 구분해서 지정해야 한다.

 

 

전화 앱 연동하기

전화 또한 안드로이드의 기본 앱으로 많이 사용한다. 앱에 전화번호가 있다면 전화를 걸 수 있도록 해주는 것이 기본이다. 그런데 전화를 거는 기능은 전화 앱의 액티비티에 이미 구현되어 있으므로 전화 앱과 연동해서 전화번호 데이터만 넘겨주면 된다.

 

전화 앱과 연동해 전화를 거는 기능은 다음처럼 퍼미션을 설정해야 한다.

// 전화를 거는 퍼미션 설정
<uses-permission android:name="android.permission.CALL_PHONE" />

 

그리고 다음과 같은 인텐트를 선언해서 시스템에 전달해야 한다.

// 전화 앱을 실행하는 인텐트
val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:02-120")) 
					// 데이터 정보는 tel:로 시작해야 하고 이어서 전화번호 명시해야 함
startActivity(intent)

전화 앱과 연동하려면 인텐트의 액션 문자열을 intent.ACTION_CALL로 지정해야 한다. 그리고 데이터 정보는 tel:로 시작해야 하고 이어서 전화번호를 명시해야 한다. 그러면 자동으로 해당 전화번호로 전화를 건다. 

 

 

공부일기

더보기

호흡이 너무 길다. 뒤에 20장인 챕터도 잘 해낼 수 있을 거라고 믿는다.

'깡샘 코틀린' 카테고리의 다른 글

17-2 파일에 보관하기  (0) 2023.07.12
17-1 저장소에 데이터 보관하기  (0) 2023.07.09
16-1 콘텐츠 프로바이더 이해하기  (0) 2023.07.08
15-4 잡 스케줄러  (0) 2023.07.08
15-3 백그라운드 제약  (0) 2023.07.07