2026/02/28

오늘의 이야기




<알림수집기앱 :  이하 사용자앱으로 표시 >:는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다.
○ 이 개인정보처리방침은 2022년 4월 1부터 적용됩니다.
 
제1조(개인정보의 처리 목적)

<사용자 앱>은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다.

 
○ 제공되는 앱의 사용자 확인을 위해서 만 사용 됩니다.

제2조(개인정보의 처리 및 보유 기간)

① <사용자 앱>은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다.

② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.

  • 1.<앱 사용자 회원가입 및 관리>
  • <앱 사용자 회원가입 및 관리>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<이 앱의 사용기간 동안>까지 위 이용목적을 위하여 보유.이용됩니다.
  • 보유근거 : 이 앱의 사용자 확인을 위해서

제3조(개인정보의 제3자 제공)

① <사용자 앱>은(는) 개인정보를 제1조(개인정보의 처리 목적)에서 명시한 범위 내에서만 처리하며, 정보주체의 동의, 법률의 특별한 규정 등 「개인정보 보호법」 제17조 및 제18조에 해당하는 경우에만 개인정보를 제3자에게 제공합니다.
② < billcorea >은(는) 다음과 같이 개인정보를 제3자에게 제공하지 않습니다.
 
제4조(개인정보처리 위탁)

① <사용자 앱>은(는) 원활한 개인정보 업무처리를 위하여 다음과 같이 개인정보 처리업무를 위탁하지 않습니다.
②  위탁업무의 내용이나 수탁자가 변경될 경우에는 지체없이 본 개인정보 처리방침을 통하여 공개하도록 하겠습니다.



제5조(정보주체와 법정대리인의 권리·의무 및 그 행사방법)


① 정보주체는 우리집에 대해 언제든지 개인정보 열람·정정·삭제·처리정지 요구 등의 권리를 행사할 수 있습니다.
② 제1항에 따른 권리 행사는우리집에 대해 「개인정보 보호법」 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 우리집은(는) 이에 대해 지체 없이 조치하겠습니다.
③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다.이 경우 "개인정보 처리 방법에 관한 고시(제2020-7호)" 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.
④ 개인정보 열람 및 처리정지 요구는 「개인정보 보호법」 제35조 제4항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다.
⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다.
⑥ billcorea 은(는) 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다.



제6조(처리하는 개인정보의 항목 작성)

①<사용자 앱>은(는) 다음의 개인정보 항목을 처리하고 있습니다.

  • 1<앱 사용자 회원가입 및 관리 >
  • 필수항목 : 식별기호(uuid token), 이메일주소,  
  • 소셜 로그인 : 이메일주소, 별명(별칭), 프로필 이미지 링크

제7조(개인정보의 파기)

① <사용자 앱> 은(는) 개인정보 보유기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다.

② 정보주체로부터 동의받은 개인정보 보유기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다.
1. 법령 근거 : 관련법규 적용 사항 없음
2. 보존하는 개인정보 항목 : 없음

③ 개인정보 파기의 절차 및 방법은 다음과 같습니다.
1. 파기절차
<사용자 앱> 은(는) 파기 사유가 발생한 개인정보를 선정하고, < billcorea > 의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다.
2. 앱의 사용자 설정에서 로그인 관련 정보 삭제 버튼을 이용하여 삭제 할 수 있습니다.

제8조(개인정보의 안전성 확보 조치)

<사용자 앱>은(는) 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.

1. 내부관리계획의 수립 및 시행
개인정보의 안전한 처리를 위하여 내부관리계획을 수립하고 시행하고 있습니다.

2. 개인정보에 대한 접근 제한
개인정보를 처리하는 데이터베이스시스템에 대한 접근권한의 부여,변경,말소를 통하여 개인정보에 대한 접근통제를 위하여 필요한 조치를 하고 있으며 침입차단시스템을 이용하여 외부로부터의 무단 접근을 통제하고 있습니다.

3. 비인가자에 대한 출입 통제
개인정보를 보관하고 있는 물리적 보관 장소를 별도로 두고 이에 대해 출입통제 절차를 수립, 운영하고 있습니다.

제9조(개인정보 자동 수집 장치의 설치•운영 및 거부에 관한 사항)
<사용자 앱> 은(는) 정보주체의 이용정보를 저장하고 수시로 불러오는 '쿠키(cookie)'를 사용하지 않습니다.
 
제10조 (개인정보 보호책임자)
① <사용자 앱> 은(는) 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.

  • ▶ 개인정보 보호책임자
  • 성명 :강동엽
  • 직책 :manager
  • 직급 :manager
  • 연락처 : 0504-0662-8122, help@billcorea.com

※ 개인정보 보호 담당부서로 연결됩니다.

  • ▶ 개인정보 보호 담당부서
  • 부서명 :manager
  • 담당자 :강동엽
  • 연락처 :  0504-0662-8122, help@billcorea.com

② 정보주체께서는 우리집 의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. 우리집 은(는) 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다.
 
제11조(개인정보 열람청구)
정보주체는 「개인정보 보호법」 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다.
<사용자 앱>은(는) 정보주체의 개인정보 열람청구가 신속하게 처리되도록 노력하겠습니다.

  • ▶ 개인정보 열람청구 접수·처리 부서
  • 부서명 : manager
  • 담당자 : 강동엽
  • 연락처 :  0504-0662-8122, help@billcorea.com




제12조(권익침해 구제방법)


정보주체는 개인정보침해로 인한 구제를 받기 위하여 개인정보분쟁조정위원회, 한국인터넷진흥원 개인정보침해신고센터 등에 분쟁해결이나 상담 등을 신청할 수 있습니다. 이 밖에 기타 개인정보침해의 신고, 상담에 대하여는 아래의 기관에 문의하시기 바랍니다.

1. 개인정보분쟁조정위원회 : (국번없이) 1833-6972 (www.kopico.go.kr)
2. 개인정보침해신고센터 : (국번없이) 118 (privacy.kisa.or.kr)
3. 대검찰청 : (국번없이) 1301 (www.spo.go.kr)
4. 경찰청 : (국번없이) 182 (ecrm.cyber.go.kr)

「개인정보보호법」제35조(개인정보의 열람), 제36조(개인정보의 정정·삭제), 제37조(개인정보의 처리정지 등)의 규정에 의한 요구에 대 하여 공공기관의 장이 행한 처분 또는 부작위로 인하여 권리 또는 이익의 침해를 받은 자는 행정심판법이 정하는 바에 따라 행정심판을 청구할 수 있습니다.

※ 행정심판에 대해 자세한 사항은 중앙행정심판위원회(www.simpan.go.kr) 홈페이지를 참고하시기 바랍니다.

제13조(개인정보 처리방침 변경)
 
① 이 개인정보처리방침은 2022년 4월 1부터 적용됩니다.





반응형






























오늘의 이야기

https://medium.com/androiddevelopers/jetpack-compose-before-and-after-8b43ba0b7d4f



 


Jetpack Compose — Before and after


How the build speed, APK size and source line count changed after migrating the Tivi sample app to Jetpack Compose


medium.com




...  인용글의 하단 


 


과일 비유는 제쳐두고, 저에게 가장 큰 시사점은 Compose가 대부분의 개발자 메트릭에 긍정적인(또는 중립적인) 영향을 미칠 것이라는 것입니다. 이를 염두에 두고 Compose를 사용하여 개발자 생산성이 크게 향상됨에 따라 Compose가 Android에서 UI 개발의 미래라는 것은 당연하게 느껴집니다.


 


...


 


다른 이야기는 다 접어두고 라도... 인용글의 마지막 부분에 있는 저 문장 (개발자 생산성이 크게 향상됨에 따라 Compose가 Android 에서 UI개발의 미래라는 것은 당연하게 느껴진다.) 때문에 라도...


 


새로 배워야할 것만 같다.


 


여태 만들어 왔던 앱들은 layout 기반에서 UI을 구현하다 보니... 나름의 편리함(?)도 있기는 했으나, 그 안에서만 구현을 해 왔던 것이다.  그래서 다이나믹 하고 유연한 UI구현은 좀 어렵지 않나 하는 생각이 든다. 


물론 그것이 나쁘다거나, 그런 건 아니다.  다만, 앞으로 가는 길에 도움이 되려면 새로운 것들에 대한 도전(?)을 해야 한다는 것이지...


 


그러다 보니, 난 아직도 내가 그리는 화면들이 그렇게 퀄리티 있어 보이지 않는 다는 것에 동의 한다.  구글링 만으로 배워 보기에는 아직 무리수가 있는 것 같기는 하다.


 


그래도 이제라도 배워 봐야 겠다.


 


안드로이드 개발자 가이드에서 퍼옴...  


Jetpack 라이브러리 모든 라이브러리 탐색



* 인기 있고 자주 사용하는 라이브러리가 먼저 나열됩니다.










































































































































































































































































































































































































activity *Activity에 기반하여 빌드된 구성 가능한 API에 액세스합니다.
appcompat *이전 API 버전의 플랫폼에서 새 API에 액세스할 수 있습니다(대부분 머티리얼 디자인 사용).
appsearch *사용자를 위한 맞춤 인앱 검색 기능을 빌드합니다.
camera *모바일 카메라 앱을 빌드합니다.
compose *모양과 데이터 종속 항목을 설명하는 구성 가능한 함수를 사용하여 프로그래매틱 방식으로 UI를 정의합니다.
databinding *레이아웃의 UI 구성요소를 선언적 형식을 사용하여 앱의 데이터 소스에 결합합니다.
fragment *Activity 내에서 호스팅되는 여러 개의 독립적인 화면으로 앱을 분할합니다.
hilt *Dagger Hilt의 기능을 확장하여 androidx 라이브러리에서 특정 클래스의 종속 항목 삽입을 사용 설정합니다.
lifecycle *활동이나 프래그먼트의 현재 수명 주기 상태를 기반으로 동작을 조정할 수 있는 수명 주기 인식 구성요소를 빌드합니다.
Material Design Components *맞춤설정이 가능한 Android용 모듈식 머티리얼 디자인 UI 구성요소입니다.
navigation *인앱 UI를 빌드 및 구조화하고 딥 링크를 처리하며 화면 간에 이동합니다.
paging *페이지에 데이터를 로드하여 RecyclerView에 표시합니다.
room *SQLite 데이터베이스에서 지원하는 영구 데이터를 생성, 저장 및 관리합니다.
test *Android에서 테스트합니다.
work *지연 가능한 제약 조건 기반 백그라운드 작업을 예약하고 실행합니다.
adsPlay 서비스 유무에 관계없이 광고 ID를 가져옵니다.
annotation도구 및 다른 개발자가 앱의 코드를 이해하는 데 도움이 되는 메타데이터를 노출합니다.
arch.coreLiveData와 함께 사용할 수 있는 JUnit 테스트 규칙을 포함한 다른 arch 종속 항목의 도우미입니다.
asynclayoutinflater레이아웃을 비동기식으로 확장하여 UI에서 버벅거림을 방지합니다.
autofill확장 힌트를 통해 자동 완성 정확도를 개선합니다.
benchmarkAndroid 스튜디오 내에서 코드 성능을 정확하게 측정합니다.
biometric생체 인식 또는 기기 사용자 인증 정보로 인증하고 암호화 작업을 실행합니다.
browser사용자의 기본 브라우저에 웹페이지를 표시합니다.
car-appAndroid Auto용 내비게이션, 주차, 충전 앱을 빌드합니다.
cardview둥근 모서리와 그림자로 머티리얼 디자인 카드 패턴을 구현합니다.
collection크기가 작은 기존 컬렉션 및 새로운 컬렉션이 메모리에 미치는 영향을 줄입니다.
compose.animationJetpack Compose 애플리케이션에서 애니메이션을 빌드하여 사용자 환경에 풍부함을 더합니다.
compose.compilerKotlin 컴파일러 플러그인으로 최적화를 사용 설정하고 @Composable 함수를 변환합니다.
compose.foundation즉시 사용 가능한 구성요소를 사용해 Jetpack Compose 애플리케이션을 작성하고 기초를 확장해 나만의 디자인 시스템 요소를 빌드합니다.
compose.material즉시 사용 가능한 머티리얼 디자인 구성요소로 Jetpack Compose UI를 빌드합니다. 이는 Compose를 고급 수준에서 사용하는 첫 단계이며, www.material.io에 설명된 것과 동일한 구성요소를 제공합니다.
compose.material3차세대 머티리얼 디자인인 머티리얼 디자인 3 구성요소로 Jetpack Compose UI를 빌드합니다. 머티리얼 3은 업데이트된 테마 설정 및 구성요소, Material You 맞춤설정 기능(동적 색상 등)을 포함하며 새로운 Android 12의 시각적 스타일 및 시스템 UI와 일관되도록 설계되었습니다.
compose.runtimeCompose의 프로그래밍 모델과 상태 관리를 위한 기본 구성요소 및 타겟팅용 Compose 컴파일러 플러그인 핵심 런타임입니다.
compose.ui레이아웃, 그리기, 입력 등 기기와 상호작용할 때 필요한 Compose UI의 기본적인 구성요소입니다.
concurrent코루틴을 사용하여 작업을 기본 스레드 외부로 이동하고 ListenableFuture를 활용합니다.
constraintlayout상대 위치에 따라 유연한 방식으로 위젯의 위치와 크기를 지정합니다.
contentpager백그라운드 스레드에서 ContentProvider 데이터를 로드하고 페이징합니다.
coordinatorlayoutAppBarLayout 및 FloatingActionButton과 같은 최상위 애플리케이션 위젯을 배치합니다.
core최신 플랫폼 기능과 API를 타겟팅하는 동시에 이전 기기도 지원합니다.
cursoradapterListView 위젯에 커서 데이터를 노출합니다.
customview맞춤 뷰를 구현합니다.
Datastore비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장하여 SharedPreferences의 일부 단점을 극복합니다.
documentfile파일 문서를 확인합니다.
draganddrop다른 앱 또는 앱 내에서 드래그 앤 드롭 데이터를 허용하고 일관된 드롭 타겟 어포던스를 표시합니다.
drawerlayout머티리얼 디자인 창 위젯을 구현합니다.
dynamicanimation물리학 기반 애니메이션 API를 사용하여 자연스러운 애니메이션을 만듭니다.
emoji현재 및 이전 기기에 이모티콘을 표시합니다.
emoji2현재 및 이전 기기에 이모티콘을 표시합니다.
enterprise엔터프라이즈 지원 애플리케이션을 만듭니다.
exifinterface이미지 파일 EXIF(데이터) 태그를 읽고 씁니다.
games앱에서 기본적으로 Android 게임 SDK를 사용하여 Frame Pacing과 같은 복잡한 게임 작업을 실행합니다.
glanceJetpack Compose 스타일의 API를 사용하여 원격 표면의 레이아웃을 빌드합니다.
gridlayout그리드 레이아웃을 구현합니다.
health플랫폼에 구애받지 않는 방식으로 성능 기준에 적합한 상태 애플리케이션을 만듭니다.
heifwriterAndroid 기기에서 사용 가능한 코덱을 사용하여 이미지 또는 이미지 컬렉션을 HEIF 형식으로 인코딩합니다.
interpolator이전 플랫폼에서 애니메이션 보간기를 사용합니다.
jetifier지원 중단된 라이브러리의 종속 항목을 그에 대응하는 AndroidX 종속 항목으로 이전하는 독립형 도구입니다.
leanbackD패드에 적합한 위젯과 템플릿 프래그먼트를 사용하여 Android TV 기기용 앱을 개발합니다.
legacy이 아티팩트와 아티팩트의 클래스는 지원 중단되었습니다. Android 8부터 백그라운드 확인 제한으로 인해 이 클래스는 더 이상 유용하지 않습니다.
loader구성 변경 후에도 유지되는 UI 데이터를 로드합니다.
localbroadcastmanager이 아티팩트와 아티팩트의 클래스는 지원 중단되었습니다. 대신 LiveData 또는 반응형 스트림을 사용하세요.
media다른 앱과 미디어 콘텐츠 및 컨트롤을 공유합니다. media2로 대체되었습니다.
media2다른 앱과 미디어 콘텐츠 및 컨트롤을 공유합니다.
media3*미디어 사용 사례를 위한 지원 라이브러리입니다.
mediarouter일반 사용자 인터페이스를 사용하여 원격 수신 기기에서 미디어 표시 및 재생을 사용 설정합니다.
multidexAndroid 5 이전 기기에 여러 dex 파일이 있는 애플리케이션을 배포합니다.
측정항목애플리케이션의 다양한 런타임 측정항목을 추적 및 보고합니다.
palette이미지에서 대표적인 색상 팔레트를 추출합니다.
percentlayout이 아티팩트와 아티팩트의 클래스는 지원 중단되었습니다. 대신 ConstraintLayout 및 연결된 레이아웃을 사용하세요.
preference기기 저장소와 상호작용하거나 UI를 관리할 필요 없이 대화형 설정 화면을 빌드합니다.
print앱에서 사진, 문서 및 기타 그래픽과 이미지를 인쇄합니다.
profileinstallerART에서 읽을 컴파일 추적을 라이브러리가 미리 채울 수 있습니다.
recommendationAndroid TV 런처 홈 화면에 콘텐츠를 홍보합니다.
recyclerview메모리 사용량을 최소화하면서 UI에 많은 양의 데이터를 표시합니다.
remotecallback개발자가 PendingIntent를 더 쉽게 제공할 수 있는 래퍼를 만듭니다.
resourceinspectionAndroid 스튜디오의 Live Layout Inspector에 맞춤 뷰 속성을 표시합니다.
savedstate프로세스가 종료되면 UI 상태를 저장하고 프로세스가 다시 시작되면 복원하는 플러그형 구성요소를 작성합니다.
security키를 안전하게 관리하고 파일 및 sharedpreference를 암호화합니다.
sharetarget바로가기를 직접 공유 타겟으로 사용하기 위해 이전 버전과의 호환성을 제공합니다.
slice앱 외부에 템플릿 UI 요소를 표시합니다.
slidingpanelayout슬라이딩 창 UI 패턴을 구현합니다.
startup앱을 시작할 때 구성요소를 초기화하는 간단하고 성능 기준에 부합하는 방법을 구현합니다.
sqlite로컬 SQLite 데이터베이스로 작업합니다. 가능하면 대신 Room을 사용하세요.
swiperefreshlayout스와이프하여 새로고침 UI 패턴을 구현합니다.
textclassifier대화, 링크, 선택 및 기타 유사한 구성을 텍스트로 식별합니다.
tracing시스템 트레이스 버퍼에 트레이스 이벤트를 씁니다.
transition시작 및 종료 레이아웃에서 UI 모션에 애니메이션을 적용합니다.
tvproviderAndroid TV 채널을 제공합니다.
vectordrawable벡터 그래픽을 렌더링합니다.
versionedparcelable프로세스 간에 전달되거나 안전하게 유지될 수 있는 안정적이고 간단한 바이너리 직렬화 형식을 제공합니다.
viewpager스와이프할 수 있는 형식으로 뷰 또는 프래그먼트를 표시합니다. 가능하면 viewpager2를 대신 사용하세요.
viewpager2스와이프할 수 있는 형식으로 뷰 또는 프래그먼트를 표시합니다.
wearWear OS by Google 스마트시계용 애플리케이션을 만듭니다.
wear.compose웨어러블용 기기, 크기, 모양 및 탐색 동작을 지원하는 기능을 제공하여 웨어러블 기기용 Jetpack Compose 애플리케이션을 작성합니다.
wear.tilesWear OS by Google 스마트시계용 애플리케이션을 만듭니다.
wear.watchfaceWear OS by Google 스마트시계용 애플리케이션을 만듭니다.
webkitAndroid 5 이상에서 최신 WebView API로 작업합니다.
window폴더블 기기와 같은 다양한 기기 폼 팩터를 지원할 수 있습니다.



 


https://developer.android.com/jetpack



 


Android Jetpack  |  Android 개발자  |  Android Developers


Jetpack은 개발자가 관심 있는 코드에 집중할 수 있도록 권장사항 준수, 상용구 코드 축소, 모든 Android 버전과 기기에서 일관되게 작동하는 코드 작성을 돕는 라이브러리 모음입니다.


developer.android.com




 


하나씩 배워보자. 시간이 걸리더라도...





오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기

https://firebase.google.com/docs/app-check/android/safetynet-provider?authuser=0&hl=ko 



 


Android에서 SafetyNet으로 앱 확인 활성화  |  Firebase Documentation


Join Firebase at Google I/O online May 11-12, 2022. Register now 이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 Android에서 SafetyNet으로 앱 확인 활성화 이 페이지에서는 내장 Saf


firebase.google.com




 


앱을 만들면 당연히 데이터를 저장할 방법을 생각하게 되고, 그러다 보면 데이터를 여러사람이 같이 사용하는 걸 생각하게 된다. 


 


서버를 가지고 있다면 고민할 이유가 없기도 하겠지만, 말이지... 서버가 없으니, online 으로 데이터를 저장할 수 있는 걸 생각 하지 않을 수 없다.  그래서 생각한 것은 firebase ... 그 안에서 realtime database 을 사용해서 이런 저런 앱을 만들다 보면... 


 


firebase 규칙 이슈 메일



 


firebase 규칙 설정



가끔 메일 온다.  저장 규칙에 대한 이슈로...


 


 


 


 


 


 


 


 


 


 


 


 


 


이렇게 규칙을 설정하면


 


등록된 사용자만 사용할 수 있도록 구성이 되어 있음에도


여러 사용자가 공유를 하게 되서 그런건가 


 


규칙에 대한 보안 규칙을 말하는 메일이 오게 된다.


 


 


 


그래서 생각한 것은 app check 을 구현해 보는 것이라고 생각을 했다.  물론 이렇게 하는 게 맞는 건가는 아직 모른다.


일단 다른 생각이 나지 않으니 이렇게 구현해 보아야 겠다는 생각이 들었다. 


 


구현한을 해 보자... 먼저 gradle 파일에 가이들 보면서 추가 하기...


 


dependencies {

...
implementation 'com.google.firebase:firebase-appcheck-safetynet:16.0.0-beta05'
implementation 'com.google.firebase:firebase-appcheck-debug:16.0.0-beta05'
...

}

debug 는 실제 릴리즈 이후에는 필요가 없을 것 같기는 한데, 개발 단계에서 realtime database 가 사용 되지 않는 현상이 발생하기 때문에 필요하다.


 


다음은 activity 에서 앱 초기화...


 


    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

...

FirebaseApp.initializeApp(this@MainActivity)
val firebaseAppCheck = FirebaseAppCheck.getInstance()
firebaseAppCheck.installAppCheckProviderFactory(
// SafetyNetAppCheckProviderFactory.getInstance() // 실물폰에 적용시
DebugAppCheckProviderFactory.getInstance() // AVD 로 테스트 할 때 적용
)

...

}

개발 모드에서는 위 예시와 같이 debugappcheck 을 사용하도록 하고 실제 배포하게 되면 그때는 safetynetappcheck 을 사용하면 될 것 같다.  이렇게 해서 앱을 실행 하면 logcat 에서 다음을 찾을 수 있다. 


 




your project 뒤에 나오는 토큰 값...  그것을 firebase 의 app check 의 설정에 등록해 주면 된다. 


 


앱 설정



firebase의 콘솔에서 앱 체크에 앱을 등록하고 뒤에 있는 ... 메뉴를 클릭하면 디버그 토큰 관리가 있고. 


 


디버그 토근 관리



디버그 토큰 추가를 클릭해서 나오는 화면에 앞의 logcat 에서 확인된 토근을 입력하고 저장해준 다음 완료를 클릭하면 등록이 완료 된다. 


 


적용 선택



다음은 그 아래에 있는 제품 카테고리에서 내가 사용하고자 하는 realtime database 을 클릭해서 적용이 되도록 설정해 주면 된다.    적용하고 나서 앱을 실행해본 결과는 아래 그림과 같이 나타난다.


 


설정된 호출과 그렇지 않을때 호출을 확인해 볼 수 있다.


 


적용결과



 


이 방법으로 목적했던 바가 이루어지는 가는 아직 알 수 없으나, 혹여나 나중에 알게 되면 이글을 수정할 생각이다.


 





오늘의 이야기


#스하리1000명프로젝트

스치니들!
내가 만든 이 앱은, 내 폰에 오는 알림 중에서 중요한 키워드가 있는 경우
등록해둔 친구에게 자동으로 전달해주는 앱이야 📲

예를 들어, 카드 결제 알림을 와이프나 자녀에게 보내주거나
이번 달 지출을 달력처럼 확인할 수도 있어!

앱을 함께 쓰려면 친구도 설치 & 로그인해줘야 해.
그래야 친구 목록에서 서로 선택할 수 있으니까~
서로 써보고 불편한 점 있으면 알려줘 🙏

👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao





오늘의 이야기

오늘은 인앱 업데이트에 도전해 보자.


 


https://developer.android.com/guide/playcore/in-app-updates?hl=ko 



 


인앱 업데이트  |  Android 개발자  |  Android Developers


인앱 업데이트 사용자는 기기에서 앱을 최신 상태로 유지하여 새로운 기능을 사용해 보고 성능 향상과 버그 수정을 통한 이점도 얻을 수 있습니다. 사용자 중에는 기기가 무제한 데이터에 연결


developer.android.com




 


앱을 만들어 게시를 하다보면 업데이트를 하게 되는 데, 내가 만든 앱의 사용자들이 업데이트를 잘 하고 있는 가에 대한 고민을 하게 된다.  그러나 사용자들은 그다지 업데이트에 관심이 없다.  자동으로 해 주면 좋은 거고, 아니면 말고, 혹시 android 설정에서 충전중 자동 업데이트를 설정해 두었다면 모르겠으나...


 


개발자 가이드의 설명은 AppUpdateManager 을 활용하는 방법에 대한 설명을 하고 있다. 이걸 보면서 그냥 따라하기를 해 보았다.


 


...

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.util.Log;

import androidx.appcompat.app.AlertDialog;

import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.UpdateAvailability;
import com.google.android.play.core.tasks.Task;
import com.kakao.sdk.common.KakaoSdk;
import com.nari.notify2kakao.InitActivity;
import com.nari.notify2kakao.R;

public class GlobalApplication extends Application {
private static volatile GlobalApplication obj = null;
private static volatile Activity currentActivity = null;
private static String TAG = "GlobalApplication";
static AppUpdateManager appUpdateManager ;
static int MY_REQUEST_CODE = 1000;

@Override
public void onCreate() {
super.onCreate();
obj = this ;
KakaoSdk.init(this, getString(R.string.kakao_app_key));
}

public static void doUpdateStart(AppUpdateInfo appUpdateInfo) {
AlertDialog.Builder builder = new AlertDialog.Builder(getCurrentActivity(), R.style.DialogTheme);
builder.setTitle(getCurrentActivity().getString(R.string.beginInAppUpdate))
.setMessage(getCurrentActivity().getString(R.string.msgRunUpdateStart))
.setPositiveButton(getCurrentActivity().getString(R.string.OK), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.e(TAG, "startUpdateFlowForResult");
try {
appUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
AppUpdateType.IMMEDIATE,
// The current activity making the update request.
getCurrentActivity(),
// Include a request code to later monitor this update request.
MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}

}
});
AlertDialog dialog = builder.create();
dialog.show();
}

public static GlobalApplication getGlobalApplicationContext() {
return obj;
}

public static Activity getCurrentActivity() {
return currentActivity;
}

// Activity가 올라올때마다 Activity의 onCreate에서 호출해줘야한다.
public static void setCurrentActivity(Activity currentActivity) {
GlobalApplication.currentActivity = currentActivity;

appUpdateManager = AppUpdateManagerFactory.create(getCurrentActivity());
Log.e(TAG, "checkUpdate start...");
// 업데이트를 확인하는 데 사용하는 인텐트 개체를 반환합니다.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
// 플랫폼이 지정된 유형의 업데이트를 허용하는지 확인합니다.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
Log.e(TAG, "updateAvailability=" + appUpdateInfo.updateAvailability());
Log.e(TAG, "IMMEDIATE=" + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE));

if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// 이 예는 즉시 업데이트를 적용합니다. 유연한 업데이트를 적용하려면
// 대신 AppUpdateType.FLEXIBLE을 전달합니다.
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// 업데이트를 요청합니다.
doUpdateStart(appUpdateInfo);
}
});

}
}

소스 예시는 알림을 카카오톡으로 보내는 앱에 사용된 GlobalApplication 안에 들어가 있는 내용이다.  다만, 아직 이렇게 구현된 소스가 정상적으로 구동 되고 있는 지는 알 수 없다. 아직 구동이 되어 보지 않아서...


 


난 오늘도 뭔 삽질을 하고 있는 건지 알 수 없다.  나중에 또 이글을 업데이트 할 날이 오기를 기다리며...


 


 





오늘의 이야기

예전 부터 만들어 관리하던 앱이 있는데...  알림을 카카오톡을 보내주는 ... 카카오에서 api 을 upgrade 하고 있어서 나도 해 보기로 했다.


 


먼저 gradle 파일의 변화


 


변경전


implementation group: project.KAKAO_SDK_GROUP, name: 'usermgmt', version: project.KAKAO_SDK_VERSION
implementation group: project.KAKAO_SDK_GROUP, name: 'kakaotalk', version: project.KAKAO_SDK_VERSION
implementation group: project.KAKAO_SDK_GROUP, name: 'friends', version: project.KAKAO_SDK_VERSION

변경후


implementation "com.kakao.sdk:v2-user:2.9.0" // 카카오 로그인
implementation "com.kakao.sdk:v2-talk:2.9.0" // 친구, 메시지(카카오톡)
implementation "com.kakao.sdk:v2-story:2.9.0" // 카카오스토리
implementation "com.kakao.sdk:v2-link:2.9.0" // 메시지(카카오링크)
implementation "com.kakao.sdk:v2-navi:2.9.0" // 카카오내비

 


이제 로그인 시도해 보는 방법의 차이 


 


변경전에는 제공된 login activity 을 이용해서 login 을 시도 하면 계정으로 로그인하는 것만을 지원했다면... 


변경후에는 


 


if (UserApiClient.getInstance().isKakaoTalkLoginAvailable(InitActivity.this)) {
UserApiClient.getInstance().loginWithKakaoTalk(InitActivity.this, new Function2<OAuthToken, Throwable, Unit>() {
@Override
public Unit invoke(OAuthToken oAuthToken, Throwable throwable) {
if (throwable != null) {
Log.e(TAG, throwable.getMessage());
return null;
} else {
Intent intent = new Intent(InitActivity.this, MonthlyMain.class);
startActivity(intent);
finish();
}
return null;
}
});
} else {
Log.e(TAG, "keyHash=" + Utility.INSTANCE.getKeyHash(InitActivity.this));
UserApiClient.getInstance().loginWithKakaoAccount(InitActivity.this, prompts, new Function2<OAuthToken, Throwable, Unit>() {
@Override
public Unit invoke(OAuthToken oAuthToken, Throwable throwable) {
Log.e(TAG, oAuthToken.getAccessToken());
if (throwable != null) {
Log.e(TAG, throwable.getMessage());
return null;
} else {
Intent intent = new Intent(InitActivity.this, MonthlyMain.class);
startActivity(intent);
finish();
}
return null;
}
});
};

** 2022.03.24 수정 : 위 소스에서 getApplicationContext() 에서 InitActivity.this 로 수정함.


** getApplicationContext() 에 파라미터를 전달하게 되면 정상적으로 실행이 되지 않는 현상이 발견됨.


 


내폰에서 카카오톡으로 로그인이 가능한 상태 인지를 확인해 보고 가능 하다면 단순 로그인이 지원이 되고 그렇지 않을 떄면 예전 처럼 계정으로 로그인을 시도 하도록 구성할 수 있다는 차이가 생겼다.


 


친구 목록을 받아오는 방법은


 


변경전


AppFriendContext friendContext = new AppFriendContext( AppFriendOrder.NICKNAME, 0, 100, "asc");

KakaoTalkService.getInstance().requestAppFriends(friendContext,
new TalkResponseCallback<AppFriendsResponse>() {
@Override
public void onNotKakaoTalkUser() {
// 발신자가 카카오톡 유저가 아님
Log.e(TAG, "onNotKakaoTalkUser") ;
}

@Override
public void onFailure(ErrorResult errorResult) {
// 그 외 에러
Log.e(TAG, "getErrorMessage =" + errorResult.getErrorMessage());
Log.e(TAG, "getErrorCode =" + errorResult.getErrorCode());
Log.e(TAG, "getHttpStatus =" + errorResult.getHttpStatus());
Log.e(TAG, "errorResult =" + errorResult.toString());

kakaoToast.makeToast(StrValueAdd.this, errorResult.getErrorMessage(), Toast.LENGTH_LONG).show();

}

@Override
public void onSessionClosed(ErrorResult errorResult) {
// 액세스토큰 및 리프레시토큰이 만료됨. 재로그인 필요.
Log.e(TAG, "onSessionClosed" ) ;
redirectLoginActivity() ;
}

@Override
public void onSuccess(AppFriendsResponse result) {
// context의 beforeUrl과 afterUrl이 업데이트 된 상태.
appFriendInfos = result.getFriends();
adapter = new kakaoFriendinfoAdapter(appFriendInfos);

listAppFriendInfo.setAdapter(adapter);
appFriendCount = result.getTotalCount();
Log.e(TAG, "onSuccess " + result.getTotalCount()) ;
if (!"".equals(strUUID)) {
for(int i=0; i < appFriendCount ; i++) {
if (strUUID.equals(appFriendInfos.get(i).getUUID())) {
Log.e(TAG, strUUID + "/" + appFriendInfos.get(i).getUUID()) ;
appFiendIndex = i;
break ;
}
}
if (appFriendInfos != null) {
try {
kakaoUserId.setText(appFriendInfos.get(appFiendIndex).getProfileNickname());
} catch (Exception e) {
kakaoUserId.setText("");
}
} else {
kakaoUserId.setText("");
}
}
}
});

 


변경후


TalkApiClient.getInstance().friends(new Function2<Friends<Friend>, Throwable, Unit>() {
@Override
public Unit invoke(Friends<Friend> friendFriends, Throwable throwable) {

if (friendFriends != null) {
appFriends = friendFriends;
adapter = new kakaoFriendinfoAdapter(appFriends);
listAppFriendInfo.setAdapter(adapter);
appFriendCount = appFriends.getTotalCount();
Log.e(TAG, "onSuccess " + appFriends.getTotalCount()) ;
if (!"".equals(strUUID)) {
for(int i=0; i < appFriendCount ; i++) {
if (strUUID.equals(appFriends.getElements().get(i).getUuid())) {
Log.e(TAG, strUUID + "/" + appFriends.getElements().get(i).getUuid()) ;
appFiendIndex = i;
break ;
}
}
try {
kakaoUserId.setText(appFriends.getElements().get(appFiendIndex).getProfileNickname());
} catch (Exception e) {
kakaoUserId.setText("");
}

} else {
kakaoUserId.setText("");
}

}
return null;
}
});

훨씬 소스의 길이가 간결해진다.


 


다음은 문자공유하는 sendMessage 의 차이


 


변경전


LinkObject link = LinkObject.newBuilder().setWebUrl("https://developers.kakao.com")
.setMobileWebUrl("https://developers.kakao.com")
.build();
TextTemplate params = TextTemplate.newBuilder(strBody, link)
.setButtonTitle("OK").build();

if (!"".equals(rs.getString(5))) {
List<String> uuids = Collections.singletonList(rs.getString(5));

KakaoTalkService.getInstance().sendMessageToFriends(uuids, params, new TalkResponseCallback<MessageSendResponse>() {
@Override
public void onNotKakaoTalkUser() {
Log.e(TAG, " 발신자가 카카오톡 유저가 아님 ");
}

@Override
public void onFailure(ErrorResult errorResult) {
Log.e(TAG, " 그 외 에러 ");
}

@Override
public void onSessionClosed(ErrorResult errorResult) {
Log.e(TAG, " 액세스토큰 및 리프레시토큰이 만료됨. 재로그인 필요. ");
}

@Override
public void onSuccess(MessageSendResponse result) {
Log.e(TAG, "API 호출 성공. 일부 사용자에게는 전송이 실패했을 수 있음. ");
kakaoToast.makeToast(context, strBody, Toast.LENGTH_LONG).show();

}
});
} else {
KakaoTalkService.getInstance().requestSendMemo(new TalkResponseCallback<Boolean>() {
@Override
public void onNotKakaoTalkUser() {
Log.e(TAG, " 발신자가 카카오톡 유저가 아님 ");
}

@Override
public void onSessionClosed(ErrorResult errorResult) {
Log.e(TAG, " 그 외 에러 ");
}
@Override
public void onSuccess(Boolean result) {
Log.e(TAG, "API 호출 성공. 일부 사용자에게는 전송이 실패했을 수 있음. ");
kakaoToast.makeToast(context, "To Me : " + strBody, Toast.LENGTH_LONG).show();
}
}, params);
}

변경후


 


Link link = new Link("http://billcorea.tistory.com","http://billcorea.tistory.com", null, null);
TextTemplate textTemplate = new TextTemplate(strBody, link, null, "알림을 카톡으로");
if (!"".equals(rs.getString(5))) {
List<String> uuids = Collections.singletonList(rs.getString(5));
TalkApiClient.getInstance().sendDefaultMessage(uuids, textTemplate, new Function2<MessageSendResult, Throwable, Unit>() {
@Override
public Unit invoke(MessageSendResult messageSendResult, Throwable throwable) {
Log.e("context", messageSendResult.toString());
return null;
}
});

} else {
TalkApiClient.getInstance().sendDefaultMemo(textTemplate, new Function1<Throwable, Unit>() {
@Override
public Unit invoke(Throwable throwable) {
Log.e("context", "메모 등록");
return null;
}
});
}

수정하면서 url 등은 변경을 했지만... 훨씬 간결하게 코드를 구현할 수 있게 된 것 같다.


 


이제 안드로이드 패치가 되어도 더는 고민할 이유가 없어졌다.


 


로그인 실행된 샘플 이미지는 다음과 같이...


로그인 하기



 





오늘의 이야기


#스하리1000명프로젝트,
Kadang-kadang susah nak bercakap dengan pekerja asing kan?
Saya membuat aplikasi mudah yang membantu! Anda menulis dalam bahasa anda, dan orang lain melihatnya dalam bahasa mereka.
Ia auto-terjemah berdasarkan tetapan.
Sangat berguna untuk sembang mudah. Lihatlah apabila anda mendapat peluang!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

https://github.com/PierfrancescoSoffritti/android-youtube-player



 


GitHub - PierfrancescoSoffritti/android-youtube-player: YouTube Player library for Android and Chromecast, stable and customizab


YouTube Player library for Android and Chromecast, stable and customizable. - GitHub - PierfrancescoSoffritti/android-youtube-player: YouTube Player library for Android and Chromecast, stable and c...


github.com




 youtube 에 올라가 있는 동영상을 내 앱에서도 볼 수 있을까 하는 생각이 든다.  위 링크의 라이브러리를 이용하면


가능해 보인다.


 


도전 !


 


 


youtube play 하는 앱 만들기



 


구현할 것은 별 거 없어 보인다.   먼저 gradle 파일에 종속성을 추가한다.


 


implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:11.0.1'

이것은 위에 기술한 링크에 있는 라이브러리를 연결하기만 하면 되고 layout 은 그냥 player 만 올리면 된다. 


 


 


<?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">

<com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
android:id="@+id/youtube_player_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingClass,MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

아무것도 없이 그냥 플레이어만 있는 layout 을 만들고 activity 을 만들면 끝이다. 


 



import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.billcoreatech.myyoutube.databinding.ActivityMainBinding;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener;

public class MainActivity extends AppCompatActivity {

ActivityMainBinding binding ;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getLifecycle().addObserver(binding.youtubePlayerView);
binding.youtubePlayerView.addYouTubePlayerListener(new AbstractYouTubePlayerListener() {
@Override
public void onReady(@NonNull YouTubePlayer youTubePlayer) {
super.onReady(youTubePlayer);
String videoId = "cd_X7Os3OVE";
youTubePlayer.loadVideo(videoId, 0);
}
});
}
}

저기 나오는 viodeId 는 youtube 에 올라간 영상의 링크 값이다.  다만, youtube 에 올라간 영상의 경우에는 youtube 설정에서 다른 player 에서 플레이가 되도록 설정하는 게 필요해 보인다.


 


 



예제 앱 동영상이 나오는 화면


 


이것으로 youtube 의 영상 플레이어 구현은 끝.





오늘의 이야기

이전에 posting 했던 pdf 공유 하기이 다음 이야기 정도가 될 것 같다.  요 몇일은 앱 수리를 하느라.. 좀 


아무튼 이번에 작업하면서 찾아낸 것에 대해서 기억을 정리해 두어야 겠다. 


 


이번에 하게 된 일은 pdf 을 만들어서 공유를 하는 것인데, 방법이 2가지 정도는 되는 것 같다. 


 


첫번째는 이전 posting 에서 처럼 공유할 파일만 지정해서 ACTION_SEND 액션을 실행하는 방법이고, 이번에는 그것을 특정앱으로 한정해 보는 것이다. 


 


public void sharePdf(String sPackageName) {
File pdfFile = new File(getCacheDir(), "/" + fileName + ".pdf");
Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName()+".fileProvider", pdfFile);

Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("application/pdf");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setPackage(sPackageName);
if (getPackageList(sPackageName)) {
shareIntent.setPackage(sPackageName);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "전달할 제목");
shareIntent.putExtra(Intent.EXTRA_TEXT, "전달할 메시지 내용.");
startActivity(Intent.createChooser(shareIntent, "알림TITLE"));
} else {
String url = "market://details?id=" + sPackageName;
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
}
}

public boolean getPackageList(String packageName) {
boolean isExist = false;

PackageManager pkgMgr = getPackageManager();
List<ResolveInfo> mApps;
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mApps = pkgMgr.queryIntentActivities(mainIntent, 0);

try {
for (int i = 0; i < mApps.size(); i++) {
Log.e("PackageName=", "" + mApps.get(i).activityInfo.packageName);

if(mApps.get(i).activityInfo.packageName.startsWith(packageName)){
isExist = true;
break;
}
}
}
catch (Exception e) {
isExist = false;
}
return isExist;
}

이렇게 함수를 만들어 놓으면 구현 가능한 부분이 쉬워진다. 보내고 싶은 앱의 패키지 이름만 알아낸다면 그 앱으로 내가 만든 파일 (예시에서는 pdf 가 있다고 가정했다.) 을 바로 전달을 하게 되는 것이다. 


 


// com.dho.mobilefax : skt 모바일 팩스
// com.google.android.gm : gmail
// com.google.android.apps.docs : google drive 문서저장
// com.samsung.android.messaging : 삼성 문자 메시지
// com.google.android.apps.messaging : 구글 기본 메시지
// net.daum.android.mail : 다음메일
// com.kakao.talk : 카카오톡
// com.sec.print.mobileprint : 삼성모바일 프린트
// epson.print : epson print

예을 들면 이런 package name 을 찾아서 위에서 기술한 함수에 호출을 넣어 주면 된다.   그리고 위 예시 소스에서 getPackageList 함수를 이용해서 해당 앱이 사용중인 폰에 설치 되어 있는 지 확인하고 없으면 앱을 설치하도록 유도 페이지로 이동시키면 되는 것이다. 


 


 


코드 예시



 


이렇게 코드를 구현해 보았다. 





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, aplikasi mesti ada untuk kelab badminton!
👉 Main Perlawanan – Rekod Markah & Cari Lawan 🎉
Sesuai untuk mana-mana sahaja, bersendirian, bersama rakan-rakan atau dalam kelab! 🤝
Jika anda suka badminton, pasti mencubanya

Pergi ke aplikasi 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

<알림수집기앱 :  이하 사용자앱으로 표시 >:는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다. ○ ...