728x90
반응형

 

Firebase Analytics 패키지를 사용하던 중 새로운 버전이 나와 버전 업데이트를 진행했습니다.

 

Debug모드에서는 전혀 문제가 없었지만 스토어 배포를 하던 중에 'com.google.android.gms.permission.AD_ID' 권한에 대한 오류가 발생했습니다.

 

기본적으로 AD_ID 권한이 존재 하지만 광고 ID를 사용하지 않아 Paly Console에서 '광고' 앱 콘텐츠를 수정해줘야 합니다.

[!] Google Api Error: Invalid request - This release includes the com.google.android.gms.permission.AD_ID permission but your declaration on Play Console says your app doesn't use advertising ID. You must update your advertising ID declaration.

 

 

 

 

# Analytics version 20.1.1 릴리즈에서 해당 내용이 추가 되었습니다.

 

# Android 12, 13 광고 ID 요구사항 입니다.

https://developers.google.com/interactive-media-ads/docs/sdks/android/dai/android-12

 

Android 12 and 13 targeting requirements  |  IMA DAI SDK for Android  |  Google Developers

Send feedback Android 12 and 13 targeting requirements Stay organized with collections Save and categorize content based on your preferences. Advertising ID If your app uses the IMA SDK version 3.25.1 or higher, the SDK already automatically declares the c

developers.google.com

 

 

 

 

# 콘텐츠 수정 

Google Play Console에 진입해줍니다.

https://play.google.com/console

 

Google Play Console | Google Play Console

앱 및 게임이 성장할 수 있도록 사용자에게 도달하고 사용자 참여를 유도하는 데 도움이 될 도구, 프로그램, 통계를 이용하세요.

play.google.com

 

 

 

개발자 계정을 선택하고, 수정할 앱을 선택합니다.

 

앱 상세에 들어가면 좌측 하단에 '정책 및 프로그램' 메뉴에서 '앱 콘텐츠' 메뉴로 진입합니다.

 

 

 

'광고 ID' 항목에서 관리를 선택합니다.

 

 

'앱에서 광고 ID를 사용하나요?' 항목에서 '' 를 선택하고 아래 애널리틱스를 체크 하고 저장합니다.

 

 

# 마무리

위 변경된 정책은 심사를 거치게 됩니다.

 

심사에서 승인되면 권한에 대한 오류가 해결됨을 확인할 수 있습니다.

728x90
반응형
728x90
반응형

 

WebView를 구현할 때 onReceivedSslError 오류를 무시하거나 구현하지 않고 PlayConsole에 출시를 하면 아래와 같이 업데이트 거부가 됩니다.

 

Play Console에 표시된 기한이 지난 후에도 보안 취약점이 수정되지 않은 앱은 모두 삭제가 되니 필수로 체크를 해야합니다.

 

 

PlayConsole에 들어가보면 아래와같이 경고 문구가 보여집니다.

 

 

 

관리자 이메일로 들어갔을때 정확한 오류문구와 해결을 위한 link가 있기 때문에 어렵지 않게 처리할 수 있습니다.

https://support.google.com/faqs/answer/7071387?hl=ko

 

앱의 WebView SSL 오류 핸들러 알림 해결 방법 - Google 고객센터

도움이 되었나요? 어떻게 하면 개선할 수 있을까요? 예아니요

support.google.com

 

 

 

 

WebChromeClient 를 설정하는 곳에서 onReceivedSslError 메서드를 override합니다.

 

인증서가 요구사항을 충족할 때 proceed()를 호출하고 그렇지 않으면 cancel()을 호출하는 코드를 작성하면 됩니다.

override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
    val builder: AlertDialog.Builder = AlertDialog.Builder(context)
    builder.setMessage("이 사이트의 보안 인증서는 신뢰하는 보안 인증서가 아닙니다. 계속하시겠습니까?")
    builder.setPositiveButton("계속하기", object: DialogInterface.OnClickListener {
        override fun onClick(p0: DialogInterface?, p1: Int) {
            handler!!.proceed()
        }
    })
    builder.setNegativeButton("취소", object: DialogInterface.OnClickListener {
        override fun onClick(p0: DialogInterface?, p1: Int) {
            handler!!.cancel()
        }
    })
    val dialog: AlertDialog = builder.create()
    dialog.show()
}

 

 

 

 

lambda 식으로 처리한다면 더 짧게 구현할 수 있습니다.

override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
    val builder: AlertDialog.Builder = AlertDialog.Builder(context)
    builder.setMessage("이 사이트의 보안 인증서는 신뢰하는 보안 인증서가 아닙니다. 계속하시겠습니까?")
    builder.setPositiveButton("계속하기") { _, _ -> handler!!.proceed() }
    builder.setNegativeButton("취소") { _, _ -> handler!!.cancel() }
    val dialog: AlertDialog = builder.create()
    dialog.show()
}

 

 

 

 

WebView SSL을 사용하는 모든 앱에 영향을 주는것이 아니지만 보안 정책을 준수하도록 권장하고 있습니다.

 

취약점이 발생이 되면 개발자 배포 계약을 위반하는 제품으로 간주될 수 있다고 하니 주의해야겠습니다.

 

728x90
반응형
728x90
반응형

 

토이프로젝트를 플레이스토어에 올려놓고 반년만에 시간을 내서 오류 업데이트를 하려고 했습니다.

 

수정 및 테스트를 완료하고 bundle & apk 를 추출하려고 하는데 keystore 비밀번호가 기억이 나지 않았습니다.

 

새로 다시 만들어야 하나라는 생각과 열심히 구글링을 통해 엄청난 방법을 찾았습니다.

 

 

 

 

 

Google 에서 제공하는 jar를 통해 비밀번호를 찾는 방식입니다.

java 7을 필요로 하며 해당 jar 파일과 key.jks, 그리고 찾을 비밀번호를 나열한 txt파일만 있으면 됩니다.

 

https://code.google.com/archive/p/android-keystore-password-recover/wikis/HowTo.wiki

 

Google Code Archive - Long-term storage for Google Code Project Hosting.

 

code.google.com

실행방법 

java -jar AndroidKeystoreBrute.jar <args>

가능한 인수

-m <1..3> Method -k <path> path to your keystore -d <path> dictionary (for method 2 and 3) -p use common replacements like '@' for 'a'(for method 3) WARNING - very slow!! -start <String> sets the start String for the password (for brute force) -w writes a new keystore with same password than the key -h prints helpscreen

무차별 대입

java -jar AndroidKeystoreBrute_v1.05.jar -m 1 -k <...keystore> -start AAAAAA

사전 대입

java -jar AndroidKeystoreBrute_v1.05.jar -m 2 -k "C:\\mykeystore.keystore" -d "wordlist.txt"

스마트 단어 목록 대입

java -jar AndroidKeystoreBrute_v1.05.jar -m 3 -k "C:\\mykeystore.keystore" -d "wordlist.txt"

 

 

 

 

 

위와 같이 3가지 방식을 지원합니다.

 

일반적으로 스마트 단어 목록으로 찾는걸 권장한다고 하여 같은 방법으로 진행하였습니다.

 

wordlist.txt 파일은 사진과 같이 엔터로 나열하면 됩니다.

 

 

 

 

이렇게 명령어를 입력해주시면 jar 가 실행됩니다.

java -jar [AndroidKeystoreBrute.jar 위치] -m 3 -k "key.jks 위치" -d "wordlist.txt 위치"

 

 

 

아래와같이 wordlist.txt에 입력되었던 문구를 알맞게 구성하여 찾게됩니다.

Number of keys in keystore: 1
Found alias: jeju b04.jks
Creation Date: Tue Jul 16 11:33:58 KST 2019

Start smart wordlist attack on key!!

Capitalize first letter
Current Pass: a || est. 0 Pass/Sec

Current Pass: 123412341234123458 || est. 31889 Pass/Sec

Current Pass: 4Asdfasdfb049 || est. 33290 Pass/Sec

Current Pass: 71Asdfasdf6 || est. 33841 Pass/Sec

...

Got Password in 36 seconds
Password is: xxxx for alias xxx.jks

For updates visit http://code.google.com/p/android-keystore-password-recover/

 

 

 

 

다른방식으로는 AndroidStudio의 Log를 분석하기도 하지만 위의 방법이 간단한것 같습니다.

 

만약 keystore 비밀번호를 분실하셨다면 낙담하지 마시고 꼭 찾으셨으면 좋겠습니다.

 

728x90
반응형
728x90
반응형

 

Android Studio 버전이 올라가면 Gradle 버전도 올라가고 다른 Library도 버전이 올라가게 됩니다.

 

아래와 같이 AndroidX 같은 오류가 발생하기도 합니다.

Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
	is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
	Suggestion: add 'tools:replace="android:appComponentFactory"' to <application> element at AndroidManifest.xml:14:5-68:19 to override.

 

 

 

 

 

특정 Library는 AndroidX를 사용하는데 그걸 사용하는 프로젝트가 androidx를 사용한다는 처리가 되지 않았기 때문인데요.

 

gradle.properties 에서 아래와 같이 2줄을 추가해주면 오류를 해결할 수 있습니다.

android.useAndroidX=true
android.enableJetifier=true

 

 

 

 

 

AndroidX에 관련된 내용입니다.

 

https://developer.android.com/jetpack/androidx

 

AndroidX 개요  |  Android 개발자  |  Android Developers

AndroidX는 Android 팀이 Jetpack 내에서 라이브러리를 개발, 테스트, 패키징, 버전 관리, 출시하는 데 사용하는 오픈소스 프로젝트입니다. AndroidX는 기존 Android 지원 라이브러리를 크게 개선하여 만들어졌습니다. AndroidX는 지원 라이브러리와 마찬가지로 Android OS와 별도로 배송되며 Android 버전 전체에서 이전 버전과의 호환성을 제공합니다. AndroidX는 동일한 기능과 새로운 라이브러리를 제공하여 지원 라

developer.android.com

 

728x90
반응형
728x90
반응형

Android 9.0이상 버전이 출시되었는데도 Google은 아직 라이브러리를 수정하지 않았습니다.

 

한창 이슈였던 부분이었는데 문서화도 하지 않고 그냥 오류만 발생했습니다.

 

Apache http는 android 6.0 부터 삭제가 되었지만 이를 준수하지 않아 큰 문제를 만들기도 하였습니다.

 

아직도 구버전의 google map 을 사용하거나 이슈를 직감하지 못했던 분들에게 도움이 되고자 해결방법을 알아보겠습니다.

2019-12-23 10:56:02.355 20618-20812/xxx.xxx.xxx E/AndroidRuntime: FATAL EXCEPTION: Thread-17
    Process: xxx.xxx.xxx, PID: 20618
    java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/ProtocolVersion;
        at br.b(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):2)
        at bq.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):3)
        at bs.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):20)
        at com.google.maps.api.android.lib6.drd.ak.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):6)
        at aw.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):13)
        at aw.run(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):49)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.http.ProtocolVersion" on path: DexPathList[[zip file "/data/user_de/0/com.google.android.gms/app_chimera/m/00000043/MapsDynamite.apk"],nativeLibraryDirectories=[/data/user_de/0/com.google.android.gms/app_chimera/m/00000043/MapsDynamite.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at w.loadClass(:com.google.android.gms.dynamite_dynamiteloader@19831065@19.8.31 (100400-0):16)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at br.b(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):2) 
        at bq.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):3) 
        at bs.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):20) 
        at com.google.maps.api.android.lib6.drd.ak.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):6) 
        at aw.a(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):13) 
        at aw.run(:com.google.android.gms.dynamite_mapsdynamite@19831065@19.8.31 (100400-0):49) 

 

 

 

 

1. AndroidManifest.xml 에 uses-library 추가

기존 google maps library 를 그대로 사용하는 방법입니다.

 

AndroidManifest.xml파일에서 application 수준에 uses-library 를 추가 해주면 됩니다.

<application>
	...
    
	<uses-library
            android:name="org.apache.http.legacy"
            android:required="false" />
            
</application>

 

 

 

2. Google maps 라이브러리 업데이트

다른 방법으로는 수정된 google maps library 를 사용하면 됩니다.

com.google.android.gms:play-services-maps:16.x.x

 

 

 

 

이제 다시 build를 하면 정삭적으로 실행이 됩니다.

 

728x90
반응형
728x90
반응형

 

Android Studio 가 업데이트 됨에 따라 Gradle 버전도 올라가고, 최소 TargetSdkVersion 도 자연스럽게 올라갑니다.

 

회사 내부에서만 사용하는 앱은 HTTPS 가 아닌 HTTP로만 통신을 하고 있습니다.

 

즉 SSL인증서 적용을 하지 않은건데 targetSdkVersion 28 이상에서는 HTTP 사용을 보안정책에 의해 허용하지 않고 있습니다.

 

Android Build를 진행한다면 아래와 같은 오류가 발생하면서 통신이 거부 됩니다.

java.net.UnknownServiceException: CLEARTEXT communication to 0.0.0.0 not permitted by network security policy

 

 

 

 

 

그래도 내부 프로젝트만 사용한다면 인증서가 필요없으니 강제로 보안정책을 허용 해주도록 하겠습니다.

 

app -> manifests -> AndroidManifest.xml 을 open 합니다.

 

android 수준에서 usesCleartextTraffic 이라는 속성을 true로 해주면 보안정책을 허용해주게 됩니다.

<application
	...
    android:usesCleartextTraffic="true">
    
    ...
    
</application>

 

 

 

 

 

저장을 하고 다시 build를 해봅시다.

 

정상적으로 통신이 되는것을 확인하셨다면 끝난겁니다.

 

한줄의 추가로 SSL인증서를 무시했지만 내부에서만 사용하는 서비스에만 사용을 권해 드립니다.

 

728x90
반응형
728x90
반응형

 

Layout에 이미지나 텍스트를 넣었지만 너무 내용물이 많아 화면에 넘어가는걸 처리하기 위해서 ScrollView Component 를 사용합니다.

 

거의 긴 내용이 필요한 화면에서는 필수라고 할 수 있죠.

 

일반적으로는 내용이 길어도 화면에 들어가면 상단부터 보여지게 됩니다.

 

 

 

 

하지만 ScrollView안에 RecyclerView를 넣었을때가 문제 입니다.

 

화면에 들어가면 ScrollView가 로드 된 후 RecyclerView가 로드 되기 때문에 RecyclerView가 로드된 부분에서 보여지게 됩니다.

 

의외로 이런 화면 구성이 간혹 기획되곤 합니다..

 

ScrollView 안에 RecyclerView가 있어도 상단에 보여지도록 처리를 해보겠습니다.

 

ScrollView Component 를 id 로 scrollView라고 정의 해보겠습니다.

android:id="@+id/scrollView"

 

 

 

ScrollView 자동스크롤

ScrollView.FOCUS_DOWN 은 하단으로 위치 하게 되는 옵션이고, ScrollView.FOCUS_UP 은 상단으로 위치 하게 되는 옵션입니다.

 

이외에도 여러가지 옵션이 있지만 여기서 다루지는 않겠습니다.

 

Kotlin 사용

scrollView.post {
	scrollView.fullScroll.ScrollView.FOCUS_UP)
}

 

Java로 사용

ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView);
scrollView.post(new Runnable() {
	@Override
    public void run() {
    	scrollView.funnScroll(ScrollView.FOCUS_UP);	
    }
});

 

 

 

로드가 된 후 맨 상단으로 가고 싶을 땐 Thread 처리하지 않는다면 옵션이 적용되지 않습니다.

 

특정 Component Click 이벤트가 발생하면 스크롤이 되도록 처리 해도 됩니다.

 

다양하게 사용할 수 있으니 많은 응용을 해보세요.

 

728x90
반응형
728x90
반응형

 

새로운 Android Studio 버전을 설치 하였거나 포멧을 한 뒤에 기존에 NDK를 사용하던 프로젝트를 열고 빌드 했을때 아래와 같은 에러메시지가 출력됬습니다.

 

 

 

진짜 안드로이드 에러는 검색말고는 절대 혼자 해결할 수 없는것 같네요.

 

문제는 NDK 관련 SDK 설치가 되어있지 않아서 발생된 오류로 NDK 를 설치해서 처리해보도록 하겠습니다.

 

Android Studio 메뉴에서 Preferences 로 이동합니다.

 

 

 

Appearance & Behavior > System Settings > Android SDK 메뉴를 클릭합니다.

 

우측 화면에서 SDK Tools 탭을 클릭하면 아래와 같은 화면이 나옵니다.

 

 

CMake 와 NDK 가 설치되지 않아 설치를 진행하였습니다.

 

 

 

NDK 버전이 높으면 Gradle 버전도 높아져야 하기 때문에 상황에 맞게 대처를 해야될것같습니다.

 

위와 같은 방법으로도 처리가 안될 수 있으니 에러로그를 천천히 살펴 보시기 바랍니다.

728x90
반응형
728x90
반응형

 

웹 & 모바일 개발을 할때 한국에서만 사용하는게 아닌 글로벌 버전으로 개발을 할수도 있습니다.

 

예전엔 똑같은 파일을 만들어서 문구를 바꾸고 개발도 했었지요.

 

Android에선 기기 설정 언어에 따라 텍스트 언어를 간단하게 변경할 수 있습니다.

 

 

 

 

 

프로젝트를 만들고 res -> values -> strings.xml 이라는 Resource 파일이 기본적으로 생성되어 있습니다.

 

 

 

 

각 화면마다 "문구" 를 작성하는게 아닌 strings.xml 에 정의하여 사용할 수 있습니다.

 

언어별 테스트를 진행하기 위해  새로운 string을 추가하여 greeting이라는 이름으로 만들어 주었습니다.

 

 

 

 

MainActivity.xml 에 기본적으로 TextView 태그안에 "Hello World" 라는 문구가 있었지만 아래와 같이 변경해주었습니다.

 

android:text="@string/greeting" 이라고 작성해주면 strings.xml 에 정의되어있던 greeting 의 내용을 보여줍니다.

 

지금은 strings.xml 한개이다보니 Build를 하면 "Hello World" 만 보여지게됩니다.

 

 

 

 

확인이 되었다면 이젠 다른 나라의 언어에 해당하는 strings.xml 을 추가해보겠습니다.

 

기존 strings.xml 을 열어서 우측 상단의 Open editor 를 클릭합니다.

 

 

 

 

기본 한개의 언어로 되어있는게 보여질겁니다.

 

+ , - 옆에 지구 모양의 아이콘을 클릭합니다.

 

 

 

 

다양한 국가를 선택할 수 있는데 원하는 국가를 검색해서 클릭하면 됩니다.

 

저는 Korean 을 선택하였습니다.

 

 

 

 

선택이 되면 아래와같이 Default ValueKorean 으로 분류되어 보여집니다.

 

 

 

 

"안녕" 이라는 문구를 추가하였는데 이 문구는 Android 언어설정에서 대한민국 & 한국어 으로 되어있으면 보여지는 문구입니다.

 

 

 

 

 

언어를 추가하면 폴더 구조도 변경이 되어지는데 strings.xml 이 추가되었습니다.

 

 

 

 

strings.xml (ko-rKR) 파일을 열면 "안녕" 이라는 string 태그가 추가가 되어져있습니다.

 

 

 

 

지금은 한국어만 사용한다고 해도 언젠가는 해외로 진출하면 사용하지 않을까요?

 

간단하게 다국어를 지원하는 앱을 만들어 보았습니다.

 

728x90
반응형
728x90
반응형

 

Android 화면을 회전하거나 멀티화면으로 보여진다면 Activity는 onDestroy를 수행하고 다시 onCreate를 호출합니다.

 

그 사이에 입력했던 값들이나 변수로 담았던 값들이 사라지게 됩니다.

 

이럴때 InstanceState 함수를 호출하여 값을 저장하고 다시 화면이 그려질때 불러오게됩니다.

 

 

 

 

override fun onSaveInstanceState(outState: Bundle?)

 

outState.put 함수를 통해 값을 저장합니다.

 

저장된 값을 화면이 다시 그려질때 불러오면 됩니다.

 

 

 

 

override fun onRestoreInstanceState(saveInstanceState: Bundle?)

 

saveInstanceState.get 함수를 통해 값을 불러옵니다.

 

각각의 필요한 값을 저장하고 불러오는 코드를 넣어주면 화면전환이 되거나 멀티화면으로 전환이 되었을때에도 값을 잘 출력할 수 있습니다.

 

 

728x90
반응형

+ Recent posts