안드로이드 개발 팁 #92 - assets 폴더의 PDF 파일을 외부 앱으로 열기
assets 폴더의 PDF 파일을 외부 앱으로 열기
No. 92
2025. 01. 31 (금) | Written by @dorian-mobileapp
개요
- PDF 파일을 assets 폴더로부터 외부 저장소에 복사
- PDF 파일 URI 생성 코드 작성
- Intent 활용하여 PDF 파일 여는 코드 작성
- PDF 파일을 외부 앱으로 여는 함수 작성
- 액티비티 또는 컴포저블 함수에서 PDF 파일 열기
PDF 파일을 assets 폴더로부터 외부 저장소에 복사
- asset 폴더의 파일을 앱의 cache 폴더로 복사
fun Context.copyFileToCacheDir(fileName: String) {
val file = File(this.cacheDir, fileName)
this.assets.open(fileName).use { inputStream ->
FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
}
- PDF 파일이 cache 폴더에 복사되었는지 확인하는 확장 함수도 추가
fun Context.isFileCopiedToCacheDir(fileName: String): Boolean {
val file = File(this.cacheDir, fileName)
return file.exists()
}
PDF 파일 URI 생성 코드 작성
- AndroidManifest.xml 파일의 application 요소 안에 provider 요소 추가
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
- {모듈}/src/main/res/xml/file_paths.xml 파일 추가
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="." />
</paths>
- PDF 파일 URI 생성 코드 작성
val fileUri: Uri = FileProvider.getUriForFile(
context,
"${context.packageName}.provider", // FileProvider authority
File(context.cacheDir, pdfFileName)
)
Intent 활용하여 PDF 파일 여는 코드 작성
- 필수 플래그
- FLAG_GRANT_READ_URI_PERMISSION:
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = fileUri // 위에서 정의한 fileUri
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION // 필수
})
PDF 파일을 외부 앱으로 여는 함수 작성
- openExternalAppToShowPDF()
- 작성 위치: 뷰모델 클래스
- 역할
- 외부 앱으로 PDF 파일 열기 실행
- 외부 앱 부재시 실패하여 false 리턴
- 리턴 값
- true: 파일 열기 성공
- false: PDF 열 외부 앱 부재로 인해 파일 열기 실패 (ActivityNotFoundException)
fun openExternalAppToShowPDF(
context: Context,
pdfFileName: String
): Boolean {
return try {
if (!context.isFileCopiedToCacheDir(pdfFileName)) {
context.copyFileToCacheDir(pdfFileName)
}
val fileUri: Uri = FileProvider.getUriForFile(
context,
"${context.packageName}.provider", // FileProvider authority
File(context.cacheDir, pdfFileName)
)
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = fileUri
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
})
true
}
catch (e: ActivityNotFoundException) {
e.printStackTrace()
false
}
}
액티비티 또는 컴포저블 함수에서 PDF 파일 열기
- 위에서 작성한 openExternalAppToShowPDF 함수 호출
- 이 함수가 false 리턴하면, 내부에서 PDF 실행 고려해볼 수 있음
val isPDFOpened = viewModel.openExternalAppToShowPDF(
LocalContext.current,
pdfFileName = pdfFileName
)
if (!isPDFOpened) {
// 외부 앱 부재시 내부에서 PDF 파일 여는 코드 작성 (추후 포스팅 예정)
}
지난 안드로이드 개발 팁
- #91 - 커스텀 AlertDialog 컴포저블 함수 만들기
- #90 - AlertDialog 띄우기 (Jetpack Compose)
- #89 - 프리뷰를 가능케 하는 커스텀 팝업 컴포저블 함수 (Jetpack Compose)
- #88 - 벡터 이미지에 tint color 미적용 방법 (Jetpack Compose)
- #87 - 컴포저블 함수들이 뷰모델을 공유하는 방법
- #86 - 바텀시트 구현 방법
- #85 - 코드 난독화 적용
- #84 - 스팀잇 프로필 이미지를 로딩하는 ImageView 클래스의 확장 함수 loadSmallSteemitProfileImage
- #83 - 마진을 설정하는 View 클래스의 확장 함수 setMargin(left, top, right, bottom)
- #82 - Map 클래스에서 데이터를 읽는 확장 함수 read
- #81 - Android API 타겟 버전 34로 상향 후, 추가 수정할 사항들
- #80 - 액티비티에서 설정 화면 실행 방법 (1)
- #79 - (개념정리) 서비스란?
- #78 - 구글 로그인 적용을 위한 SHA 인증서 등록
- #77 - View 객체의 post 메소드를 실행해야 할 경우
- #76 - Firebase의 Crashlytics 적용 방법
- #75 - n초 뒤 자동으로 다음 화면으로 넘어가는 코드를 코루틴으로 개발시 주의점
- #74 - assets 폴더의 오디오 파일을 재생하는 MediaPlayer 클래스의 확장함수
- #73 - assets 폴더의 오디오 파일 재생 준비하는 MediaPlayer 클래스의 확장함수
- #72 - ConstraintLayout에서 자식 뷰의 z 순서 정하기
- #71 - Serializable 객체를 간단히 읽을 수 있는 Intent 클래스의 확장함수 구현
- #70 - 2가지 조건에 따라 배경색을 다르게 설정하기 위한 selector 리소스 정의 방법
- #69 - ToggleButton 뷰에 적용된 그림자 효과 제거
- #68 - 타겟팅 버전 33 변경 후, 발생하는 SecurityException 해결 방법 (from registerReceiver)
- #67 - 인텐트에 데이터를 읽거나 기록할 때 발생하는 BadParcelableException 예외
- #66 - Deprecated 코드 개선: Intent 클래스의 getSerializableExtra 메소드
- #65 - 화면 전체를 차지한 뷰를 위로 밀어 올리는 애니메이션 리소스
- #64 - android.database.Cursor 클래스의 확장 함수
- #63 - 모서리가 둥근 사각형
- #62 - 키보드 숨김 실행 방법 그리고 이를 실행하는 확장 메소드
- #61 - 타겟 SDK 버전을 34로 설정 후 수정해야 하는 코드
- #60 - (ChatGPT 활용) 아이디에 마스크 적용하기
- #59 - ChatGPT로 간단한 코딩은 질문으로 물어보자
- #58 - TextView 뷰에 스크롤 적용 방법
- #57 - JSON 포맷의 문자열을 GSON 라이브러리 제공 JsonObject 타입으로 변환
- #56 - 앱이 백그라운드 모드에서도 라이브 데이터 관찰 방법
- #55 - 프래그먼트에서 코루틴 플로우 수집 주기 정하기
- #54 - 떠 있는 키보드 숨기는 방법
- #53 - 1줄의 텍스트를 입력받는 EditText 뷰에서 엔터 키 누름 처리 방법
- #52 - 마크다운 텍스트를 웹뷰로 보여주기 위해 추가로 설정해야 할 것
- #51 - 마크다운 텍스트를 HTML 텍스트로 변환한 후 스타일 적용 방법
- #1 ~ #50
Layout provided by Steemit Enhancer hommage by ayogom
Posted through the ECblog app (https://blog.etain.club)
안녕하세요.
SteemitKorea팀에서 제공하는 'steemit-enhancer'를 사용해 주셔서 감사합니다. 개선 사항이 있으면 언제나 저에게 연락을 주시면 되고, 관심이 있으신 분들은 https://cafe.naver.com/steemitkorea/425 에서 받아보실 수 있습니다. 사용시 @응원해 가 포함이 되며, 악용시에는 모든 서비스에서 제외될 수 있음을 알려드립니다.
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.
안녕하세요.
이 글은 SteemitKorea팀(@ayogom)님께서 저자이신 @dorian-mobileapp님을 응원하는 글입니다.
소정의 보팅을 해드렸습니다 ^^ 항상 좋은글 부탁드립니다
SteemitKorea팀에서는 보다 즐거운 steemit 생활을 위해 노력하고 있습니다.
이 글은 다음날 다시 한번 포스팅을 통해 소개 될 예정입니다. 감사합니다!
Upvoted! Thank you for supporting witness @jswit.