박상권의 삽질블로그

[안드로이드/Android]전화수신시 전화번호 가져와서 팝업으로 띄우는 방법 본문

IT/Android-TIP (한글)

[안드로이드/Android]전화수신시 전화번호 가져와서 팝업으로 띄우는 방법

박상권 2015. 12. 18. 19:17

안드로이드 개발자들이 모여있는 오픈채팅방에 참여해보세요 .
Q&A 및 팁을 공유하는 방입니다..
오픈채팅방 참여



블로그를 Medium으로 옮겨서 운영하고 있습니다.
앞으로 새로운 글은 모두 미디엄 블로그를 통해서 올릴 예정입니다.
미디엄에서 다양하고 유익한 포스팅을 살펴보세요
미디엄 블로그 보기


저는 모르는 전화번호로 걸려오는 전화는 잘 받지 않습니다.

하지만 중고거래를 하기위해 중고물건을 여러 사이트에 올려두었을때는 혹시 구매자가 전화한것일수도 있기때문에 그 시기에는 걸려오는 전화를 꼭꼭 받습니다.


셀폰이라는 중고폰 거래중개서비스에서는 내가 올려둔 중고폰 판매글을 보고 구매자가 전화하는경우 아래 이미지처럼 정보를 알려줍니다.

이러한 경우 모르는 번호이더라도 내 판매글을 보고 연락한 사람임을 알 수 있기 때문에 안심하고 전화를 받을 수 있습니다.





요즘 많이 쓰고있는 후후나 후스콜 같은 경우도 이와 비슷하게 전화가 걸려오는경우를 감지해서 해당전화번호가 스팸인지 아닌지를 확인할 수 있습니다.

여기서 이용되는 안드로이드에서의 기능은 BroadcastReceiver, Service 입니다.



대부분 이러한 서비스들의 동작순서는 아래와 같습니다.


1. 전화가 오는경우 감지를 위한 BroadcastReceiver 생성 및 등록

2. 앱이 실행중이지 않은경우에도 기능을 실행하기위한 Service 생성 및 등록

3. 띄워줄 화면의 레이아웃 생성 및 추가



그럼 BroadcastReceiver, Service를 이용해서 전화가 걸려오는 경우 걸려온 전화번호를 팝업으로 띄워서 보여주는 예제를 만들어 보겠습니다.










BroadcastReceiver 


전화가 오는상황을 감지하기 위해 전화상태를 읽을수있는 권한을 Manifest에 추가해주어야 합니다.

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>


또한, receiver도 추가해 줍니다.


<receiver android:name=".IncomingCallBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>




BroadcastReceiver의 onReceive()에서 전화가 오는경우를 감지하고 Service를 실행하는 부분을 만들어 줍니다.

여기서는 통화벨이 울리는 경우 걸려온 전화번호를 알아낸뒤, 그 전화번호를 Service로 넘겨주면서 Service를 실행하도록 하겠습니다.


if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
final String phone_number = PhoneNumberUtils.formatNumber(incomingNumber);

Intent serviceIntent = new Intent(context, CallingService.class);
serviceIntent.putExtra(CallingService.EXTRA_CALL_NUMBER, phone_number);
context.startService(serviceIntent);

}


(참고)

TelephonyManager.EXTRA_STATE_IDLE: 통화종료 혹은 통화벨 종료

TelephonyManager.EXTRA_STATE_RINGING: 통화벨 울리는중

TelephonyManager.EXTRA_STATE_OFFHOOK: 통화중





#주의

정확한 이유는 알 수 없지만 실제 BroadcastReceiver가 여러번 호출되는 경우를 볼 수 있습니다.

구글링을 해보면 저뿐만 아니라 많은 사람들이 이러한 문제로 어려움을 겪고 있습니다.

이렇게되면 우리가 의도한대로 되지않고 서비스가 여러번 호출되는 상황을 발생시키기도 합니다.

이를 해결하기 위해 static 변수를 하나 두고나서 해당 변수의 상태가 변경될때에만 실제 변경됨을 감지하고 다음동작을 실행하도록 해주었습니다.

private static String mLastState;

if (state.equals(mLastState)) {
return;

} else {
mLastState = state;

}

(참고: http://mmarvick.github.io/blog/blog/lollipop-multiple-broadcastreceiver-call-state/ )












Layout


팝업으로 보여줄 layout xml을 만들어줍니다.

여기서는 간단하게 걸려온 전화번호를보여주고 팝업창을 종료할수 있는 X버튼을 만들어 두겠습니다.







Service


Manifest에 Service를 추가해줍니다.


<service android:name=".CallingService"/>


Service의 onCreate()에서 팝업의 크기와 각종 팝업과 관련된 설정들을 세팅해줍니다.

여기서는 팝업크기의 가로가 화면의 90%만 채우도록 설정해주었습니다.

그리고 위에서 만들어둔 popup layout xml을 가져옵니다.


@Override
public void onCreate() {
super.onCreate();


windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

Display display = windowManager.getDefaultDisplay();

int width = (int) (display.getWidth() * 0.9); //Display 사이즈의 90%


params = new WindowManager.LayoutParams(
width,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
PixelFormat.TRANSLUCENT);


LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
rootView = layoutInflater.inflate(R.layout.call_popup_top, null);
ButterKnife.inject(this, rootView);
setDraggable();


}



setDragable()함수에서는 팝업View에 setOnTouchListener를 추가해서 이동시킬 수 있도록 설정해줍니다.

팝업View를 누르고 움직일때마다 이동거리만큼 같이 움직여 줍니다.


private void setDraggable() {

rootView.setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);

if (rootView != null)
windowManager.updateViewLayout(rootView, params);
return true;
}
return false;
}
});

}




onStartCommand()에서 세팅해놓음 팝업View를 WindowManager에 추가해줍니다.

넘겨받은 전화번호 정보를 가져와서 팝업View의 TextView에도 세팅해줍니다.

onStartCommand()에서 START_REDELIVER_INTENT로 설정하는경우 Service가 강제종료 되더라도 다시 시작해주고 이전에 넘겨받았던 intent를 그대로 넘겨받을 수 있습니다.

여기서는 STICKY로 해주어도 크게 상관없을것 같습니다.

(Android Service 사용법)

만약 intent가 null인경우엔 팝업View를 없애줍니다.


@Override
public int onStartCommand(Intent intent, int flags, int startId) {

windowManager.addView(rootView, params);
setExtra(intent);


if (!TextUtils.isEmpty(call_number)) {
tv_call_number.setText(call_number);
}


return START_REDELIVER_INTENT;
}


private void setExtra(Intent intent) {

if (intent == null) {
removePopup();
return;
}

call_number = intent.getStringExtra(EXTRA_CALL_NUMBER);


}





서비스가 종료되거나 팝업View의 X버튼이 눌리는경우 팝업View를 없애줍니다.



@Override
public void onDestroy() {
super.onDestroy();
removePopup();
}


@OnClick(R.id.btn_close)
public void removePopup() {
if (rootView != null && windowManager != null) windowManager.removeView(rootView);
}








그렇게 완성된 화면은 아래와 같습니다.

전화가 걸려올경우 상대방의 전화번호를 넘겨받아서 팝업으로 보여주게 됩니다.





이를 잘 활용해서 가져온 전화번호를 운영중인 서비스의 서버에 요청해서 각종 정보를 가져오는 기능을 만들 수도 있습니다.

여러가지 응용된 방식으로 사용자에게 의미있는 정보를 노출해주길 바랍니다.




이것저것 다 귀찮으신분들을 위해 Github에 프로젝트를 올려두었습니다.

Github에서 다운받으셔서 테스트프로젝트 실행해보셔도 좋습니다.

Github에서 전체프로젝트 보기


(내용이 유용하셨다면 Github 오른쪽위의 [Star]버튼을 눌러주시면 감사하겠습니다

저에게는 별풍선 주시는 효과가 있어요!)

Comments