일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 전화번호 가져오기
- CODEIGNITER
- 아파치
- 가상컴퓨터
- ubuntu
- phpMyAdmin
- 리눅스
- jenkins
- 젠킨스
- 아파치 https 적용
- 웹서버
- vimrc
- 가상머신
- jenkins war
- 안드로이드
- 우분투 젠킨스 업데이트
- vi
- virtualbox
- SMS Retreiver API
- Phone Selector
- 우분투
- 인증번호 문자
- vim
- jenkins 업데이트
- Firebase
- 우분투 아파치 ssl
- 우분투 젠킨스
- Android
- php
- 버추얼박스
- Today
- Total
철스토리
안드로이드 Phone Selector, SMS Retriever API 본문
안드로이드에서 자신의 폰에서 자신의 번호를 가지고오는 방법이 존재했다.
TelephonyManager를 통해서 자신의 번호를 쉽게 가지고 올 수 있었다.
그렇지만 이것은 READ_PHONE_STATE 권한이 필요하다.
하지만 최근 READ_PHONE_STATE와 READ_SMS 권한에 대해서 구글에서 특별관리(?)가 들어갔다.
스토어에 올리지 않을것이라면 관계 없지만 플레이스토어에 앱을 등재하기 위해서는 꼭 해당 권한이 필요하지 않는다면 사용할 수 없다.
앱을 서비스 할 때 스마트폰에서 폰번호를 가지고 오거나 문자메시지에 접근해서 인증번호를 가지고와서 자동으로 인증번호의 값을 세팅해주는 작업을 많이한다.
구글에서는 이러한경우에 READ_PHONE_STATE와 READ_SMS 권한 없이도 사용가능한 api를 제공해 주었다.
1. gradle에 아래의 내용을 추가
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'
2. 폰번호를 불러오고 싶은 곳에 아래와 같이 구현
public static final int RESOLVE_HINT = 10102;
public static final String TAG = "TAG";
private void requestHint() {
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
GoogleApiClient apiClient = new GoogleApiClient.Builder(activity)
.enableAutoManage(activity, 0 /* clientId */, null)
.addApi(Auth.CREDENTIALS_API)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(apiClient, hintRequest);
try {
activity.startIntentSenderForResult(intent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RESOLVE_HINT && resultCode == Activity.RESULT_OK) {
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
// credential.getId() : +821012345678 형태로 폰 번호를 얻을 수 있음
Log.d(TAG, credential.getId());
}
}
위의 작업을 통해 폰번호를 가지고 올 수 있게 되었다.
인증 문자메시지를 자동으로 불러오는 작업 자체는 복잡하지 않은데 서버에서 문자메시지에 앱의 키 해시를 담아서 보내줘야 한다. 해당 규칙은 아래와 같다.
1. 문자메시지 전체의 길이는 140 byte를 넘으면 안된다.
2. 문자의 맨 앞에는 <#> 이 무조건 붙어야 한다. ([#] 도 가능하다고 하는거 같음... 테스트 안해봄)
3. 문자의 맨 뒤에는 앱의 키 해시가 들어가야한다. (9글자 키 해시)
키 해시를 얻는 코드
public static final String TAG = "TAG";
public static ArrayList<String> getAppSignatures(Context context) {
ArrayList<String> appCodes = new ArrayList<>();
try {
// Get all package signatures for the current package
String packageName = context.getPackageName();
PackageManager packageManager = context.getPackageManager();
Signature[] signatures = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures;
// For each signature create a compatible hash
for (Signature signature : signatures) {
String hash = getHash(packageName, signature.toCharsString());
if (hash != null) {
appCodes.add(String.format("%s", hash));
}
Log.d(TAG, String.format("이 값을 SMS 뒤에 써서 보내주면 됩니다 : %s", hash));
}
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Unable to find package to obtain hash. : " + e.toString());
}
return appCodes;
}
private static String getHash(String packageName, String signature) {
String appInfo = packageName + " " + signature;
try {
MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
// minSdkVersion이 19이상이면 체크 안해도 됨
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
}
byte[] hashSignature = messageDigest.digest();
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
// encode into Base64
String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
Log.d(TAG, String.format("\nPackage : %s\nHash : %s", packageName, base64Hash));
return base64Hash;
} catch (NoSuchAlgorithmException e) {
Log.d(TAG, "hash:NoSuchAlgorithm : " + e.toString());
}
return null;
}
위의 함수 getAppSignatures를 호출하여 로그에 "이 값을 SMS 뒤에 써서 보내주면 됩니다 : " 라고 되어 있는 부분을 확인하면 된다.
서버쪽에서 문자를 보내주는 규칙은 되었고, 안드로이드에서 처리해줄것만 남았다.
1. BroadcastReceiver를 상속받은 SmsReceiver를 생성한다.
public class SmsReceiver extends BroadcastReceiver {
public static final String TAG = "TAG";
public static final String SMSRetrievedAction = "com.google.android.gms.auth.api.phone.SMS_RETRIEVED";
@Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
Log.d("TAG", "SmsReceiver : onReceiver");
switch (status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
Log.d("TAG", "SmsReceiver : onReceiver(CommonStatusCodes.SUCCESS)");
// 본인은 문자를 받았을 때 EventBus를 통해 처리해 줬다.
// 이 부분이 문자메시지를 받은것이니 각자 message를 가공해서 숫자를 뽑아낸 다음 세팅시켜주면 될 듯 하다.
EventBus.getDefault().post(new SmsReceiverEvent(message));
break;
case CommonStatusCodes.TIMEOUT:
Log.d("TAG", "SmsReceiver : onReceiver(CommonStatusCodes.TIMEOUT)");
break;
}
}
}
}
2. AndroidManifest.xml 에 receiver 등록한다.
<receiver
android:name=".SmsReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
3. SmsReceiver를 등록해준다.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerSmsReceiver();
smsRetrieverCall();
}
private void smsRetrieverCall() {
SmsRetrieverClient client = SmsRetriever.getClient(this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "smsRetrieverCall SUCCESS");
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "smsRetrieverCall FAIL");
}
});
}
private void registerSmsReceiver() {
SmsReceiver smsReceiver = new SmsReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(SmsReceiver.SMSRetrievedAction);
registerReceiver(smsReceiver, filter);
}
4. 마지막으로 문자메시지를 받아서 처리한부분인데 본인은 이벤트버스로 처리했음
@Subscribe(threadMode = ThreadMode.MAIN)
public void setSmsReceiverEvent(SmsReceiverEvent event) {
Log.e(TAG, event.getAuthNumber());
String authNumber = event.getAuthNumber();
// 문자메시지의 내용 그대로가 넘어온다.
// 그러기 때문에 인증번호만 뽑아내는 로직은 각자가 만들어야 함
editSmsCode.setText(authNumber);
}
다음번에 까먹지 않도록 내용을 작성했다! 끝!
'안드로이드 > 프로그래밍' 카테고리의 다른 글
웹뷰에서 카메라 촬영 및 갤러리에서 사진 불러오기 기능 (0) | 2018.11.23 |
---|---|
[Android, iOS] 번역 시트 관리하기 (구글 스프레드시트) (0) | 2018.09.08 |
안드로이드 파일 및 폴더(하위폴더까지) 삭제하는 방법 (0) | 2016.12.28 |
인증서 지문 검색 SHA1 (0) | 2016.10.26 |
TabLayout (0) | 2016.10.16 |