반응형

 

안드로이드 리소스 디렉토리에서 values는 주로 문자열이나 설정값을 저장합니다.

 

다국어 관련해서는 values-"language"-"country" 디렉토리명된 string.xml 파일에 저장된다는 것은 익히 알고 있을 겁니다.

 

자세한 내용은 아래 글을 참고하시면 됩니다.

2018/09/19 - 안드로이드 다국어 지원 (support multiple language in android) - Translations Editor 사용법 포함

 

 

values-mcc 의미

values-"mcc" 디렉토리명으로 된 것은 결론적으로 말하면 어떤 국가의 통신망을 잡고 있는지에 따른 구분입니다.

 

통신망이 들어가서 혼란이 올 수도 있는데, 좀 더 쉽게 말하면 내 폰이 특정 국가에 있을 경우에 유효한 디렉토리입니다.

여기서 mcc는 Mobile Country Code의 약어인데, 국가마다 고유한 3자리 숫자가 mcc 뒤에 붙게 됩니다.

 

대한민국: 450, 미국: 310, 중국: 460, 일본: 440, 독일: 262, 캐나다: 302 ...

 

국가별 mcc값은 아래에서 검색하시면 됩니다.

https://en.wikipedia.org/wiki/Mobile_country_code

 

만약 values-mcc310 디렉토리가 있다면 이것은 미국 통신망을 잡고 있을 때 유효합니다.

 

values-mcc 활용 및 사용예

국가별 정책 적용시 대표적인 경우입니다.

위에서 언급한 다국어 지원하는 방법의 경우 해당 국가의 모국어가 아닌 외국어를 사용하는 경우에 제대로 처리되지 않습니다.

이럴경우에는 values-mcc 디렉토리로 국가별로 구분해서 값을 넣어주는 방법을 사용할 수 있습니다.

 

예로 들며 "카메라 촬영음 의무화" 정책이 있는 대한민국, 일본, 인도의 경우 아래처럼 프레임워크에서 처리되고 있습니다.

 

[기본]

/frameworks/base/core/res/res/values/config.xml  

<bool name="config_camera_sound_forced">false</bool>

 

[대한민국]

/frameworks/base/core/res/res/values-mcc450/config.xml  

<bool name="config_camera_sound_forced">true</bool>

 

[일본]

/frameworks/base/core/res/res/values-mcc440/config.xml  

<bool name="config_camera_sound_forced">true</bool>

 

[인도]

/frameworks/base/core/res/res/values-mcc404/config.xml  

<bool name="config_camera_sound_forced">true</bool>

 

위의 3개의 나라에 있을 경우 카메라로 촬영하면 프레임워크의 오디오 서비스에서 config_camera_sound_forced 값을 읽어와서 값에 따른 촬영음 처리를 합니다.

 

통신사별 처리가 필요할 때는 values-"mcc"-"mnc" 사용

참고로 mcc와 더불어 mnc를 같이 사용하기도 합니다.

mnc는 Mobile Network Code의 약어로서 통신사를 의미하며 1~2자리 숫자가 mnc 뒤에 붙게 됩니다.

values-mcc450-mnc5의 경우 대한민국의 SKT 통신사의 망을 잡고 있을 때 유효한 디렉토리입니다.

 

MTU size와 같이 통신사별 설정 값을 읽어오는 처리가 필요한 경우에 mnc를 추가해서 활용할 수 있습니다.

반응형

앱의 Android 9 Pie(이하 파이) 마이그레이션에 대해서 알아보겠습니다.

 

현재 제조사별로 플래그쉽 모델들을 필두로 파이로 출시되거나 파이로 업그레이드된 바이너리가 계속 배포되고 있습니다.

 

작년 겨울부터 적어야겠다고 생각만 하다가 주변에 조금씩 파이 폰이 보이고 나서야 갑자기 생각나서 "파이 마이그레이션"에 대해서 적고 있습니다.

 

[마이그레이션이 필요한 앱]

모든 앱이 마이그레이션이 필요한 것은 아닙니다.

파이 업그레이드된 폰에서 앱이 잘 돌아간다면 굳이 해줄 필요는 없습니다.

 

보통 제조사에서 안드로이드 OS 업그레이드를 하면 사용자 데이터와 앱은 그대로 두고 안드로이드만 상위 버전으로 바뀌게 되는 것입니다.

 

안드로이드 업그레이드가 완료되면, 프리로드되어 있는 제조사나 통신사의 앱들은 마이그레이션된 상태일 것입니다.

구글 Play에서 다운로드한 앱들 중에도 오류가 나는 것이 있고, 오류가 없는 것도 있을 것입니다.

 

오류가 없는 앱은 그냥 두면 됩니다. 마이그레이션이 필요한 앱은 안드로이드 업그레이드 이후 오류가 발생하는 앱입니다.

 

[마이그레이션 과정]

오류가 발생해서 마이그레이션이 필요한 앱은 두 가지 부류로 나눠집니다.

 

1. 파이 플랫폼의 API 사용 없이 기존 API로 오류가 수정 가능한 경우


ⓒGoogle

- 파이 이미지를 다운받아서 폰에 플래시하세요. (바이너리를 폰의 NAND 메모리에 저장함을 의미) 
 - 대상 앱이 파이 변경사항의 영향 가능성을 유의하면서 검토.
 ※ 파이 변경사항 (http://bit.ly/2JrlEtg)
  
- 파이가 적용 폰 or 에뮬레이터에 대상 앱을 설치하고 파이 변경사항에 포커스를 맞춰 검증.

- 파이 변경사항의 영향으로 오류나 동작 이상 시 해당 이슈를 수정 후 앱을 다시 빌드.
(targetSdkVersion 변경 x)

- 앱 사이닝 후 apk 게시
  (구글
Play 등록, 배포 등등) 

 

 

 

 

 

 

 

 

 

 

2. 파이 플랫폼의 API 사용해야 오류가 수정 가능한 경우 (targetSdkVersion을 28로 변경 필요하다는 의미)


ⓒGoogle

- 파이 이미지 폰에 플래시.
- 안드로이드 스튜디오 버전을 3.1이상으로 업데이트 후 파이 SDK를 다운로드.
- targetSdkVersion 28로 변경
- 대상 앱이 파이 변경사항의 영향 가능성을 유의하면서 검토. 추가로 API level 28+ 대상 파이 변경사항도 검토 (http://bit.ly/2QaYBFV)  
- 대상 앱의 파이 변경사항(API level 28+ 대상 포함) 관련 코드 수정.
- 신규 백그라운드 제한 이슈 수정
- 필요시 파이 API 신규 기능 적용
- 수정된 소스 반영 후 다시 빌드

- 파이 적용 폰으로 대상 앱 검증
- 백그라운드 제한 경로 및 파이 변경사항에 포커스를 맞춰 검증.

 

 

- 최종 업데이트 후 빌드, 검증


- 앱 사이닝 후 apk 게시
  (구글 Play 등록, 배포 등등)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

결론

 1. 파이 단말에 앱이 잘 돌아가면 그대로 두면 됩니다.

 2. 파이 단말에 앱이 문제가 있지만, 상위 API 버전으로 변경이 필요 없이 수정 가능하면 해당 문제를 수정 후 재배포합니다.

 3. 파이 단말에 앱이 문제가 있고, 상위 API 버전으로 변경이 되어야 수정할 수 있다면, targetSdkVersion을 28로 변경 후 해당 문제를 수정 후 재배포합니다.

 

위의 1, 2의 경우 구글 Play에 배포되는 앱이 아닌 경우에 해당합니다. 구글 Play에 배포되는 앱의 경우 파이 단말에서 앱이 잘 돌아가더라도 3의 경우처럼 targetSdkVersion을 28로 변경해주셔야 합니다.

 

현재(2019년 4월 19일) 구글 Play에 신규 앱 및 업데이트 필요한 앱의 경우 targetSdkVersion은 26 이상 되어야 하지만, 2019년 8월 1일부터는 신규 앱의 경우 targetSdkVersion은 28 이상이고, 2019년 11월 1일부터는 업데이트 필요한 앱의 경우에도 targetSdkVersion을 28 이상으로 변경해주어야합니다.

 

(관련 링크: https://support.google.com/googleplay/android-developer/answer/113469?hl=ko#targetsdk)

이번 글은 여기까지입니다. 읽어주셔서 감사합니다.

 


 

반응형

안녕하세요. Simple& Happy Dev입니다.

 

다국어 적용을 고려 중이신 분들께서는 기본 언어로 values 디렉터리와 values-"language" 또는 values-"language"-"country" 디렉터리에 다국어를 넣는다는 것은 알고 계실 겁니다.

 

이전에 "안드로이드 다국어 지원"에 관해서 다룬 적이 있었습니다. 혹시 잘 모르시는 분은 아랫글 참고하시길 바랍니다.

 

2018/09/19 - 안드로이드 다국어 지원 (support multiple language in android) - Translations Editor 사용법 포함

 

그런데, 다국어 지원되는 안드로이드 플랫폼 스트링으로 일부 문구를 대체할 수도 있습니다.

 

안드로이드 스튜디오에서 레이아웃 작업을 하면서 한 번쯤 본 적은 있어도 "내가 넣어준 적도 없는데, 이게 뭐지?" 하면서 그냥 넘어갔을 수도 있습니다.

 


 

안드로이드는 왜 플랫폼 스트링이 존재하는 걸까요?.

 

이는 "ooo 이(가) 응답하지 않습니다. 닫으시겠습니까?"라는 ANR 문구처럼 안드로이드 시스템에서 사용되는 UI를 지원하기 위함입니다.

 

그리고, 여러 언어 지원을 위해서 이미 번역된 스트링이 반영되어 있습니다.

 

안드로이드 스튜디오의 Device File Explorer를 실행해서 /system/framework/ 디렉터리에 가보면 framework-res.apk가 존재합니다. 이 apk를 PC로 꺼내어서 dex2jar나 apktool로 열어보면, 우측에 있는 것처럼 다국어 스트링 디렉터리가 나옵니다.

 

 

 

values에 있는 기본 언어(영어) strings.xml과 values-ko에 있는 한국어 strings.xml를 비교해보면 아래처럼 나옵니다.

타 언어들도 번역되어 있습니다. 약 1400개 이상의 스트링들입니다.

 

 

하지만, 이것들은 다 사용할 수 있는 것은 아닙니다.

접근이 허용되지 않는 리소스를 사용할 경우에 아래와 같은 빌드 에러가 나옵니다.

Android resource linking failed
Output:  \app\src\main\res\layout\activity_main.xml:10: error: resource android:string/Midnight is private.
error: failed linking file resources.

 

안드로이드 SDK를 통해서 접근할 수 있는 스트링들은 생각보다 그렇게 많지는 않습니다.

 

"yes, no, ok, cancel, copy, cut, paste, selectAll, untitled" 포함해서 27개 정도 됩니다. 

 

예를 들어서 보겠습니다. 아래 레이아웃 가운데 "확인"이라는 텍스트를 넣었고, 이것을 다국어 적용하려고 합니다.

 

TextView 속성에서 text 항목의 우측에 있는 "..." 부분을 눌러주면, 목록에서 해당 앱에서 추가해준 스트링이 나오고 그 아래에 사용할 수 있는 내장 스트링이 나옵니다. 아래 노란색 사각형에 있는 것처럼 앞에 @android가 추가되어서 @android:string/"string_name" 형식으로 레이아웃 xml에 저장됩니다.

 

 

Java로 TextView를 처리할 경우는 아래처럼 "android.R.string.ok"로 지정해주면 됩니다.

 

 

빌드해서 확인한 결과 아래처럼 "한국어", "영어", "스페인어"로 언어 변경 시 해당 언어에 맞는 스트링이 출력되고 있습니다.

  

  

 

결론

안드로이드 프레임워크에 내장된 스트링이 많이 있지만, 실제 앱 개발 시 SDK를 통해서 접근할 수 있는 플랫폼 스트링은 많지는 않습니다. 다이얼로그 등에 자주 사용되는 문구라서 이런 것에 다국어 적용 시 별도 번역 없이 바로 사용할 수 있습니다.

 

스트링 외에 이미지, 레이아웃, 사운드 등의 플랫폼 리소스도 존재하니 한 번쯤 어떤 것이 있는지 확인해보고 차후 적용도 해보시길 바랍니다.

 

아래 손전등 앱에서는 플랫폼 리소스중에 이미지(android.R.drawable.btn_star_big_on)를 사용하고 있습니다.

 

2018/10/15 - 손전등 앱 만들기 #3 - Flashlight API 이용방법 (Flashlight app using Flashlight API)

2018/10/13 - 손전등 앱 만들기 #2 - Camera2 API 이용방법 (Flashlight app using Camera2 API)

2018/10/04 - 손전등 앱 만들기 #1 - 개요 및 Camera API 이용방법 (Flashlight app using Camera API)

반응형

안녕하세요. Simple& Happy Dev입니다.

Google Play에 있는 앱이 자신의 폰에서는 검색이 되는데, 다른 사람 폰에서는 검색되지 않는 경우가 있습니다.

드물지만 같은 종류 폰임에도 이런 경우가 있습니다.

이 글에서는 이런 현상이 발생하는 이유에 대해서 설명해 드리며 앱 개발자분들을 위한 솔루션을 제시하고자 합니다. 

앱 개발해서 Google Play에 등록하시려고 하는 분들은 꼭 알고 계셔야 하는 내용입니다.

만약 내 폰에 나침반 센서(Compass sensor)가 있는데, 이것을 이용해서 응용 앱을 개발하였고, 이것을 다른 사람과 공유를 하기 위해서 Google Play에 등록했습니다. 등록된 것도 확인하였고, 다른 사람 폰에서도 검색이 되고 다운로드도 가능합니다.

그런데, 아무런 문제가 없을까요?

같은 폰을 가지고 있는 경우에게는 문제가 없을 것입니다. 나침반 센서가 있는 다른 폰에서도 문제가 없을 것입니다.

하지만, 나침반 센서가 없는 폰은 어떨까요? 아마 동작을 하지 않거나, 오류가 발생할 수도 있습니다.

그러면, 어떻게 하면 될까요?

네, 나침반 센서가 있는 사람들만 다운받을 수 있게 하면 되는 겁니다. Google Play에서 나침반 센서를 가진 폰에 나침반 센서 기능이 있는 앱을 매칭시켜주면 되는 것이죠.

정리하면 앱 매칭을 위해서 단말기 회사와 앱 개발자는 아래와 같이 해주면 됩니다.
- 단말기 회사: 해당 폰이 가진 기능(Feature)을 Google Play에 알려준다.
- 앱개발자: 해당 앱에 사용할 기능(Feature)를 Google Play에 알려준다.

Google Play에서 앱을 필터링할 수 있게 앱의 매니페스트 화일(AndroidManifest.xml)에 몇 가지 element가 존재합니다.

<uses-feature>, <uses-configuration>, <uses-sdk>, <uses-library>, <supports-screens> 등이 여기에 해당됩니다.

그럼, 위에 언급한 나침반 센서의 경우 아래와 같이 넣어주면 나침반 센서가 없는 폰에는 더 이상 검색되지 않고, 있는 폰에서만 검색됩니다.

<uses-feature android:name="android.hardware.sensor.compass" />

그러면, 특정 기기에서 검색되지 않는 이유는 앱과 단말기 간의 매칭이 되지 않았기 때문입니다.

드물지만 같은 폰인데, 검색되지 않는 경우는 어떤 경우일까요?

이런 현상이 발생한 가능성이 높은 것은 같은 폰인데 안드로이드 버전이 틀릴 경우입니다.

A폰은 안드로이드 5.0이고, B폰은 OS 업그레이드를 해서 안드로이드 6.0이라고 하면 아래와 같이 앱의 element가 추가된 경우에 이런 현상이 발생할 수 있습니다. 아래 API 레벨 23을 실행할 수 있는 최소 레벨로 정했기 때문에 API 레벨 21인 A폰에서는 검색이 되지 않습니다.

<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="23" />

앱의 기능을 지원하지 않는 폰에서 검색되지 않는 것은 당연합니다.

하지만, 위에 있는 sdk 경우처럼 특별한 사유가 없이 필터링하는 경우에 기능을 지원함에도 검색되지 않습니다.

문제가 되는 기능이 앱 일부에 해당하는 경우에 조건에 따른 기능 동작 설정 및 UI 변경작업을 하는 것도 한가지 방법입니다.

만약 조깅앱을 만들었는데, 주기능이 걸음 카운터랑 GPS로 이동한 궤적을 보여주는 것이고, 보조 기능으로 이동한 방위를 표시해주는 것이라고 하면 나침반 센서가 없는 폰에서는 보조 기능이 나오지 않도록 UI 변경을 해주면 되는 것입니다.

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
detachCompass();
}

그리고, 아래와 같이 해주면 나침반 센서가 없는 폰에서도 검색이 됩니다.

<uses-feature android:name="android.hardware.sensor.compass" android:required="false" />

여기서 질문이 나올 수 있는 게 "이렇게 feature 여부를 앱에서 처리하는 거라면 위에 android:required="false"를 하지 않고, 위의 <uses-feature> element를 삭제해도 되지 않는가?"라는 것인데요.
맞습니다. 이럴 경우에는 삭제해도 무방합니다.

하지만, Permission이 필요한 feature는 삭제할 경우(예: 카메라)에 검색에서 나오지 않아서 꼭 명시를 해줘야 합니다.

위의 나침반 센서의 경우 Permission이 필요하지 않기 때문에 저렇게 하더라도 검색되어서 나옵니다.


필터링 element를 많이 사용하게 되면 앱이 요구하는 기능과 그것을 지원하는 폰의 매칭이 더 잘 맞게 될 겁니다.

내가 만든 앱의 화면이 원하는 모양대로 보이는 폰에서만 검색되게 하려고 <supports-screens>를 사용하거나 <compatible-screens>를 사용할 수도 있습니다.

아이폰과 달리 안드로이드 폰은 여러 해상도 및 화면크기에 따른 화면비율이 차이가 나고 이를 고려한 해상도별 이미지 추가 및 Layout 처리를 해줘야 합니다.

이로 인하여 UI 구성상 저해상도는 힘들 것 같다고 생각되면, 저해상도를 가진 단말에서는 검색 안 되게 필터링하면 됩니다.

<supports-screens
android:smallScreens="false"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"

android:anyDensity="true" />

주의하셔야 할 것은 매칭을 위해서 필터링 element를 여러 개 사용하다보면 특정 기기에서 검색이 되지 않는 경우가 발생합니다. 특히, screen 관련한 element를 잘못하게 되면 기능을 지원하는데도 검색이 안 되는 경우가 있으니 주의가 필요합니다.


특정 기기에서 검색되지 않는 것을 해결하는 방법은 지원하지 않는 폰을 PC에 연결 후 아래와 같은 명령을 실행해 봅니다.

adb shell dumpsys package f > features.txt

adb shell dumpsys package l > libraries.txt

그러면, 그 폰에서 지원하는 Feature와 Library 목록이 나옵니다. 아래는 가지고 있는 폰에서 위의 명령을 실행한 결과입니다.

Features:
  android.hardware.sensor.proximity
  com.samsung.android.sdk.camera.processor
  com.sec.feature.motionrecognition_service
  android.hardware.sensor.accelerometer
  android.hardware.faketouch
  com.samsung.feature.virtualscreen
  android.hardware.usb.accessory
  android.software.backup
  android.hardware.touchscreen
  android.hardware.touchscreen.multitouch
  android.software.print
  com.samsung.feature.device_category_phone_low_end
  android.software.voice_recognizers
  android.hardware.fingerprint
  com.samsung.android.knox.knoxsdk
  com.sec.feature.slocation version=3
  com.samsung.android.sdk.camera.processor.haze
  android.hardware.opengles.aep
  com.sec.feature.fingerprint_manager_service
  com.sec.feature.overlaymagnifier
  android.hardware.bluetooth
  android.hardware.camera.autofocus
  android.hardware.telephony.gsm
  android.software.sip.voip
  com.samsung.feature.samsung_experience_mobile
  android.hardware.usb.host
  android.hardware.audio.output
  android.software.verified_boot
  android.hardware.camera.flash
  android.hardware.camera.front
  android.hardware.screen.portrait
  android.hardware.nfc
  com.sec.android.mdm
  android.software.home_screen
  com.samsung.android.sdk.camera.processor.panorama
  android.hardware.microphone
  com.samsung.android.sdk.camera.processor.effect
  android.software.vr.mode
  android.hardware.bluetooth_le
  android.hardware.touchscreen.multitouch.jazzhand
  android.software.app_widgets
  android.software.input_methods
  android.hardware.vulkan.version version=4194307
  android.software.device_admin
  android.hardware.camera
  android.hardware.screen.landscape
  com.samsung.android.authfw
  com.samsung.android.api.version.2402
  com.samsung.android.api.version.2403
  com.sec.feature.cover
  com.sec.feature.findo
  android.software.managed_users
  android.software.webview
  android.hardware.camera.any
  com.samsung.android.sdk.camera.processor.dof
  com.samsung.android.sdk.camera.processor.gif
  com.samsung.android.sdk.camera.processor.hdr
  com.samsung.android.sdk.camera.processor.lls
  android.software.connectionservice
  android.hardware.touchscreen.multitouch.distinct
  android.hardware.location.network
  com.sec.android.secimaging
  android.software.sip
  android.hardware.wifi.direct
  android.software.live_wallpaper
  android.software.freeform_window_management
  android.hardware.nfc.hcef
  android.hardware.location.gps
  com.sec.feature.fingerprint_ui version=3
  android.software.midi
  android.hardware.nfc.hce
  android.hardware.wifi
  android.hardware.location
  android.hardware.vulkan.level version=1
  android.hardware.telephony

Libraries:
  smatlib -> (jar) /system/framework/smatlib.jar
  sec_platform_library -> (jar) /system/framework/sec_platform_library.jar
  com.samsung.device -> (jar) /system/framework/com.samsung.device.jar
  SemAudioThumbnail -> (jar) /system/framework/SemAudioThumbnail.jar
  com.sec.esecomm -> (jar) /system/framework/esecomm.jar
  com.sec.android.mdm.gearpolicymanager -> (jar) /system/framework/sagearpolicymanager.jar
  vsimmanager -> (jar) /system/framework/vsimmanager.jar
  sechardware -> (jar) /system/framework/sechardware.jar
  com.google.android.media.effects -> (jar) /system/framework/com.google.android.media.effects.jar
  com.samsung.android.knox.knoxsdk -> (jar) /system/framework/knoxsdk.jar
  com.sec.android.app.multiwindow -> (jar) /system/framework/com.sec.android.app.multiwindow.jar
  seccamera -> (jar) /system/framework/seccamera.jar
  com.gsma.services.nfc -> (jar) /system/framework/com.gsma.services.nfc.jar
  secdmb -> (jar) /system/framework/secdmb.jar
  semcamera -> (jar) /system/framework/semcamera.jar
  com.samsung.bbc -> (jar) /system/framework/com.samsung.bbccommon.jar
  touchwiz -> (jar) /system/framework/touchwiz.jar
  com.android.location.provider -> (jar) /system/framework/com.android.location.provider.jar
  secimaging -> (jar) /system/framework/secimaging.jar
  secvision -> (jar) /system/framework/secvision.jar
  semextendedformat -> (jar) /system/framework/semextendedformat.jar
  sec_feature -> (jar) /system/framework/sec_feature.jar
  com.sec.android.mdm -> (jar) /system/framework/sec_edm.jar
  com.samsung.android.nfc.mpos -> (jar) /system/framework/com.samsung.android.nfc.mpos.jar
  com.android.future.usb.accessory -> (jar) /system/framework/com.android.future.usb.accessory.jar
  sws -> (jar) /system/framework/sws.jar
  saiv -> (jar) /system/framework/saiv.jar
  android.ext.shared -> (apk) com.google.android.ext.shared
  javax.obex -> (jar) /system/framework/javax.obex.jar
  secmediarecorder -> (jar) /system/framework/secmediarecorder.jar
  com.google.android.gms -> (apk) com.google.android.gms
  android.ext.services -> (apk) com.google.android.ext.services
  simageis -> (jar) /system/framework/simageis.jar
  seclvbmanager -> (jar) /system/framework/seclvbmanager.jar
  com.sec.android.app.minimode -> (jar) /system/framework/com.sec.android.app.minimode.jar
  device_api -> (jar) /system/framework/device_api.jar
  imsmanager -> (jar) /system/framework/imsmanager.jar
  scamera_sdk_util -> (jar) /system/framework/scamera_sdk_util.jar
  com.publicnfc -> (jar) /system/framework/com.publicnfc.jar
  com.dsi.ant.antradio_library -> (jar) /system/framework/com.dsi.ant.antradio_library.jar
  svemanager -> (jar) /system/framework/svemanager.jar
  org.simalliance.openmobileapi -> (jar) /system/framework/org.simalliance.openmobileapi.jar
  android.test.runner -> (jar) /system/framework/android.test.runner.jar
  sercsapi -> (jar) /system/framework/sercsapi.jar
  com.google.android.maps -> (jar) /system/framework/com.google.android.maps.jar
  com.sec.android.visualeffect -> (jar) /system/framework/com.sec.android.visualeffect.jar
  com.broadcom.nfc -> (jar) /system/framework/com.broadcom.nfc.jar
  org.apache.http.legacy -> (jar) /system/framework/org.apache.http.legacy.jar
  com.android.nfc_extras -> (jar) /system/framework/com.android.nfc_extras.jar
  com.android.media.remotedisplay -> (jar) /system/framework/com.android.media.remotedisplay.jar
  allshare -> (jar) /system/framework/allshare.jar
  com.sec.smartcard.auth -> (jar) /system/framework/secsmartcard.jar
  secimshttpclient -> (jar) /system/framework/secimshttpclient.jar
  com.android.mediadrm.signer -> (jar) /system/framework/com.android.mediadrm.signer.jar
  libvtmanagerjar -> (jar) /system/framework/libvtmanagerjar.jar
  rcsopenapi -> (jar) /system/framework/rcsopenapi.jar

Feature의 경우 android로 시작하는 것을 확인하시면 되고, Library의 경우 폰 내부적으로만 사용하는 것도 포함되어 있습니다.

앱에서 필터링한 기능이 위의 목록에 존재하는지 확인 후 해당 element를 제거 또는 수정을 해주시면 됩니다.

위에 해당하지 않는 경우에는 screen 관련한 element의 영향일 가능성이 큽니다.

특별한 경우가 아니면 compatible-screens는 사용하지 말고, supports-screens의 경우 requiresSmallestWidthDp, compatibleWidthLimitDp, largestWidthLimitDp 속성 사용 시 필터링되어서 검색되지 않는 경우가 있으니 제외하고 확인해보시길 바랍니다.

앱이 검색되지 않는 폰을 구하기 어려우면 "Google Play Console - 출시 관리 - 기기 카탈로그"에 들어가서 해당하는 기기를 찾아서 Feature와 Library를 확인할 수 있지만, 실제 기기에서 확인하는 것을 권장해 드립니다.

참고로 Google Play Console에서 폰과 앱이 매칭되지만, 검색되지 않고 제외하도록 만들 수도 있습니다.
기기 카탈로그에 해당 폰을 선택해서 "제외"를 누르시면 됩니다.

이상으로 Google Play에 등록한 앱이 특정 기기에서 검색되지 않는 이유 및 해결방법에 대해서 알아보았습니다. 

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

반응형

 

 

안드로이드 앱에 다국어 지원이라는 주제로 글을 적어봅니다.

 

문자열은 별도의 xml 파일에 저장 후 Layout이나 Java에서 이것을 이용해야 하지만, 귀찮다는 이유로 Java에 하드코딩 하는 경우도 있을 것입니다.

 

추천하는 방법은 아니지만, 앱에서 지원해야 언어가 많지 않고 굳이 Java에서 다국어 지원을 처리해야겠다 할 경우에 어떻게 하면 되는지 기술해보겠습니다.

 

간단한 테스트를 위해서 버튼을 누르면 텍스트가 출력되는 앱을 만들도록 하겠습니다.

 

언어 변경을 위해서는 "설정(Settings) 앱 - 언어(Language)" 에 가서 원하는 언어로 변경하면 되지만, 테스트를 위해서 매번 그렇게 하기에는 번거롭기 때문에 앱에서 볼륨키를 눌러서 언어를 변경하도록 예제를 만들어보겠습니다.

 

[다국어지원 - JAVA에서 하드코딩 방식]

 

레이아웃 (activity_main.xml)

 

레이아웃은 가운데 Button이 하나가 있고, 그 위에 문자열 출력을 위한 TextView가 있도록 만들었습니다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="35dp"
android:textColor="@android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</android.support.constraint.ConstraintLayout>

자바 (MainActivity.java)

실행하면 onCreate에서 TextView와 Button에 "Thanks"와 "Default(English)"가 화면에 출력됩니다.

 

dispatchKeyEvent에서는 볼륨 상하키를 누르는 방향에 따라서 언어를 지정하는 변수가 변경[각주:1]되고, 버튼을 누르면 기본 언어인 영어 "Thanks"에 해당하는 변경된 언어가 TextView에 표시됩니다.

 

package com.example.help.multilanguagetest;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
private TextView mTextView;
private Button mButton;
private int mLang;

private final static String [] THANKS = {"Thanks.", "Gracias.", "Danke.", "Merci.", "감사합니다."};
private final static String [] LANGUAGES = {"Default(English)", "Spanish", "German", "French", "한국어"};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mTextView = findViewById(R.id.textView);
mTextView.setText(THANKS[0]);

mButton = findViewById(R.id.button);
mButton.setText(LANGUAGES[0]);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTextView.setText(THANKS[mLang]);
}
});
}

@Override
public boolean dispatchKeyEvent(KeyEvent event) {

switch(event.getKeyCode()) {
case KeyEvent.KEYCODE_VOLUME_UP:
if (event.getAction() == KeyEvent.ACTION_UP) {
if (mLang < LANGUAGES.length - 1) {
mLang++;
}
mButton.setText(LANGUAGES[mLang]);
}
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (event.getAction() == KeyEvent.ACTION_UP) {
if (mLang > 0) {
mLang--;
}
mButton.setText(LANGUAGES[mLang]);
}
return true;
default:
break;
}

return super.dispatchKeyEvent(event);
}
}

프로젝트 압축화일

MultiLanguageTest_VolUpDn.zip

 

실행결과

 

 

 

※볼륨키로 언어 변경 에뮬레이팅 없이 실제 세팅에서 언어를 변경할 경우

onCreate에서 로케일의 언어 값을 읽어와서 해당하는 언어에 맞는 문자열을 세팅해주면 됩니다.

세팅에서 변경한 언어가 지원하지 않으면 기본 언어인 영어로 해줍니다.

package com.example.help.multilanguagetest;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends Activity {
TextView mTextView;
Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Locale locale;
String language;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
locale = getResources().getConfiguration().getLocales().get(0);
} else {
locale = getResources().getConfiguration().locale;
}
language = locale.getLanguage();

mTextView = findViewById(R.id.textView);
mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

}
});

switch(language) {
case "en":
mButton.setText("Default(English)");
mTextView.setText("Thanks.");
break;
case "es":
mButton.setText("Español");
mTextView.setText("Gracias.");
break;
case "de":
mButton.setText("Deutsch");
mTextView.setText("Danke.");
break;
case "fr":
mButton.setText("Français");
mTextView.setText("Merci.");
break;
case "ko":
mButton.setText("한국어");
mTextView.setText("감사합니다.");
break;
default:
mButton.setText(language);
mTextView.setText("Thanks.");
break;
}
}
}

프로젝트 압축화일

MultiLanguageTest.zip

 

 

[다국어지원 - xml 처리 방식]

 

Java에서 하드코딩 하는 방식은 가능은 하지만 지원하는 언어가 많거나 문자열이 여러 개인 경우 비효율적입니다.

 

xml에서 다국어 지원하는 방식은 기본 언어에 대해서만 작업을 해주고, 나머지 언어에 대해서는 번역업체에 아웃소싱으로 처리할 수 있기 때문에 프로젝트 진행하는 데 있어서 효율적입니다.

 

xml 방식은 기본언어의 경우 res/values 디렉터리에 strings.xml(프로젝트 생성시 기본으로 만들어짐)에 저장을 하고 추가 언어는 res/values-"language"-"country"/ 디렉터리에 언어별로 strings.xml 파일을 만들어줍니다.

 

예로 들면, 기본 언어(영어)와 한국어, 프랑스어(캐나다)를 지원하는 앱을 개발한다고 했을 경우 아래와 같이 디렉터리를 구성 후 strings.xml 파일을 만들어주면 됩니다.

res/values/strings.xml 
values-ko/strings.xml
values-fr-rCA/strings.xml

초기에는 디렉터리만 만들어주고, 파일은 기본언어의 strings.xml을 복사해서 넣어주면 됩니다.

그런 다음에 아웃소싱한 번역업체에 넘겨주어서 번역 작업을 의뢰합니다.

 

다행히도 안드로이드 스튜디오에서는 다국어 작업은 조금 편하게 할 수 있도록 Translations Editor라는 것을 가지고 있습니다. 이것을 이용하면 외부에서 언어별 디렉터리를 만들어주거나 디렉터리마다 파일을 복사해야 하는 번거로움이 해결됩니다.

 

앞서 만든 예제와 같게 xml 처리 방식으로 만들어 보겠습니다.

 

Translations Editor

 

먼저 strings.xml 편집 창에서 오른쪽 위에 있는 Open editor를 클릭하면 Translations Editor가 실행됩니다.

 

Translations Editor가 실행되면 아래와 같은 화면이 나옵니다.

 

아래 노란색 상자에 있는 버튼들에 대해 설명을 하겠습니다.

1번: 키를 추가합니다. 여기서 키는 그 문자열의 이름이 되겠습니다.

2번: 키를 삭제합니다.

3번: 로케일를 추가합니다. 지원하기 위한 언어를 추가하는 걸 말합니다.

 

한국어를 추가해보겠습니다.

3번 로케일 추가 버튼을 눌러서 한국어를 선택합니다.

 

지원하려는 나머지 언어도 추가해주면 아래와 같은 화면이 나옵니다.

언어별로 첫 번째 키(app_name)의 키값이 기본값으로 모두 채워져 있습니다.

Translations Editor가 동작하기 위해서 첫 번째 키값이 모두 채워진 상태로 시작되어야 합니다.

혹시 나중에 번역하면 넣으려고 첫 번째 키값을 삭제해버린 상태에서 Translations Editor를 종료하면, 그다음에 Translations Editor 진입하면 추가한 언어들이 나오지 않게 됩니다. (중요함)

 

번역이 필요한 키 항목은 빨간색 글자로 나타납니다.

 

 

첫 번째(app_name) 키값들은 나중에 처리하고, 두 번째(language) 키값들은 언어별 키값의 Translation 편집 창에 채워 넣습니다.

첫 번째 키값은 앱의 이름으로 번역을 하지 않고, 타 언어들에도 기본값으로 나오도록 할 예정입니다.

이때는 Untranslatable을 선택해줍니다.

 

이때 키 항목의 글자가 빨간색으로 표시되는 것은 번역이 필요 없는데 타언어들에 키값이 채워져있어서 발생하는 현상입니다. 그래서, 이 키 항목의 기본값을 제외하고 타언어들의 키값들은 삭제해줍니다. 

 

아래가 지원하는 언어의 번역을 추가해서 완료된 화면입니다.

왼쪽에 보면 지원하는 언어별 values 디렉터리가 만들어져 있는 것을 확인하실 수 있습니다.

 

레이아웃 (activity_main.xml)

앞선 JAVA에서 하드코딩 방식과 달리 레이아웃에 문자열을 바로 삽입해줍니다. "@string/키" 형태로 넣어주면 됩니다.

<android.support.constraint.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">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/thanks"
android:textColor="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="35dp"
android:text="@string/language"
android:textColor="@android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</android.support.constraint.ConstraintLayout>

자바 (MainActivity.java)

다국어 작업을 Translations Editor로 처리했기 때문에 자바 쪽에서는 처리해줘야 할 것이 없습니다.

package com.example.help.multilanguagetest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
TextView mTextView;
Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mTextView = findViewById(R.id.textView);
mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO
}
});
}
}

프로젝트 압축화일

MultiLanguageTest2.zip

 

실행결과

 

 

이상으로 "안드로이드 다국어 지원"에 대해서 알아보았습니다.

 

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.

 

 

 

  1. 설정앱(Settings) - 언어(Language) 변경을 에뮬레이팅하는 것. [본문으로]

+ Recent posts