Android
2020.04.22 / 11:17

[¾Èµå·ÎÀ̵å/Android]ÀüÈ­¼ö½Å½Ã ÀüÈ­¹øÈ£ °¡Á®¿Í¼­ Æ˾÷À¸·Î ¶ç¿ì´Â ¹æ¹ý

Äڷγª
Ãßõ ¼ö 211

Á¦°¡ ¿î¿µÇÏ°í ÀÖ´Â À¯Æ©ºê ä³Î '°³¹ßÀÚ Å×µå¹Ú'¿¡µµ ¸¹Àº °ü½É ºÎŹµå·Á¿ä.
½ºÅ¸Æ®¾÷/°³¹ßÀÚ/IT °ü·ÃµÈ ¿©·¯ ¿µ»óÀ» ¿Ã¸®°í ÀÖ½À´Ï´Ù.
¿µ»óº¸·¯°¡±â


Àú´Â ¸ð¸£´Â ÀüÈ­¹øÈ£·Î °É·Á¿À´Â ÀüÈ­´Â Àß ¹ÞÁö ¾Ê½À´Ï´Ù.

ÇÏÁö¸¸ Áß°í°Å·¡¸¦ ÇϱâÀ§ÇØ Áß°í¹°°ÇÀ» ¿©·¯ »çÀÌÆ®¿¡ ¿Ã·ÁµÎ¾úÀ»¶§´Â Ȥ½Ã ±¸¸ÅÀÚ°¡ ÀüÈ­ÇÑ°ÍÀϼöµµ Àֱ⶧¹®¿¡ ±× ½Ã±â¿¡´Â °É·Á¿À´Â ÀüÈ­¸¦ ²À²À ¹Þ½À´Ï´Ù.


¼¿ÆùÀ̶ó´Â Áß°íÆù °Å·¡Áß°³¼­ºñ½º¿¡¼­´Â ³»°¡ ¿Ã·ÁµÐ Áß°íÆù ÆǸűÛÀ» º¸°í ±¸¸ÅÀÚ°¡ ÀüÈ­Çϴ°æ¿ì ¾Æ·¡ À̹ÌÁöó·³ Á¤º¸¸¦ ¾Ë·ÁÁÝ´Ï´Ù.

ÀÌ·¯ÇÑ °æ¿ì ¸ð¸£´Â ¹øÈ£ÀÌ´õ¶óµµ ³» ÆǸűÛÀ» º¸°í ¿¬¶ôÇÑ »ç¶÷ÀÓÀ» ¾Ë ¼ö Àֱ⠶§¹®¿¡ ¾È½ÉÇÏ°í ÀüÈ­¸¦ ¹ÞÀ» ¼ö ÀÖ½À´Ï´Ù.





¿äÁò ¸¹ÀÌ ¾²°íÀÖ´Â ÈÄÈijª ÈĽºÄÝ °°Àº °æ¿ìµµ ÀÌ¿Í ºñ½ÁÇÏ°Ô ÀüÈ­°¡ °É·Á¿À´Â°æ¿ì¸¦ °¨ÁöÇؼ­ ÇØ´çÀüÈ­¹øÈ£°¡ ½ºÆÔÀÎÁö ¾Æ´ÑÁö¸¦ È®ÀÎÇÒ ¼ö ÀÖ½À´Ï´Ù.

¿©±â¼­ ÀÌ¿ëµÇ´Â ¾Èµå·ÎÀ̵忡¼­ÀÇ ±â´ÉÀº 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/ )




public class IncomingCallBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "PHONE STATE";
private static String mLastState;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public void onReceive(final Context context, Intent intent) {
Log.d(TAG,"onReceive()");
/**
* http://mmarvick.github.io/blog/blog/lollipop-multiple-broadcastreceiver-call-state/
* 2¹ø È£ÃâµÇ´Â ¹®Á¦ ÇØ°á
*/
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(mLastState)) {
return;
} else {
mLastState = state;
}
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);
}
}
}









Layout


Æ˾÷À¸·Î º¸¿©ÁÙ layout xmlÀ» ¸¸µé¾îÁÝ´Ï´Ù.

¿©±â¼­´Â °£´ÜÇÏ°Ô °É·Á¿Â ÀüÈ­¹øÈ£¸¦º¸¿©ÁÖ°í Æ˾÷âÀ» Á¾·áÇÒ¼ö ÀÖ´Â X¹öÆ°À» ¸¸µé¾î µÎ°Ú½À´Ï´Ù.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f00"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:id="@+id/tv_call_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text=""
android:textColor="#fff"
android:textSize="20sp" />
<ImageButton
android:id="@+id/btn_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:src="@drawable/abc_ic_clear_mtrl_alpha" />
</LinearLayout>
view rawcall_popup_top.xml hosted with ❤ by GitHub






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);
}





public class CallingService extends Service {
public static final String EXTRA_CALL_NUMBER = "call_number";
protected View rootView;
@InjectView(R.id.tv_call_number)
TextView tv_call_number;
String call_number;
WindowManager.LayoutParams params;
private WindowManager windowManager;
@Override
public IBinder onBind(Intent intent) {
// Not used
return null;
}
@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();
}
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;
}
});
}
@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);
}
@Override
public void onDestroy() {
super.onDestroy();
removePopup();
}
@OnClick(R.id.btn_close)
public void removePopup() {
if (rootView != null && windowManager != null) windowManager.removeView(rootView);
}
}
view rawCallingService.java hosted with ❤ by GitHub




±×·¸°Ô ¿Ï¼ºµÈ È­¸éÀº ¾Æ·¡¿Í °°½À´Ï´Ù.

ÀüÈ­°¡ °É·Á¿Ã°æ¿ì »ó´ë¹æÀÇ ÀüÈ­¹øÈ£¸¦ ³Ñ°Ü¹Þ¾Æ¼­ Æ˾÷À¸·Î º¸¿©ÁÖ°Ô µË´Ï´Ù.





À̸¦ Àß È°¿ëÇؼ­ °¡Á®¿Â ÀüÈ­¹øÈ£¸¦ ¿î¿µÁßÀÎ ¼­ºñ½ºÀÇ ¼­¹ö¿¡ ¿äûÇؼ­ °¢Á¾ Á¤º¸¸¦ °¡Á®¿À´Â ±â´ÉÀ» ¸¸µé ¼öµµ ÀÖ½À´Ï´Ù.

¿©·¯°¡Áö ÀÀ¿ëµÈ ¹æ½ÄÀ¸·Î »ç¿ëÀÚ¿¡°Ô ÀǹÌÀÖ´Â Á¤º¸¸¦ ³ëÃâÇØÁÖ±æ ¹Ù¶ø´Ï´Ù.




ÀÌ°ÍÀú°Í ´Ù ±ÍÂúÀ¸½ÅºÐµéÀ» À§ÇØ Github¿¡ ÇÁ·ÎÁ§Æ®¸¦ ¿Ã·ÁµÎ¾ú½À´Ï´Ù.

Github¿¡¼­ ´Ù¿î¹ÞÀ¸¼Å¼­ Å×½ºÆ®ÇÁ·ÎÁ§Æ® ½ÇÇàÇغ¸¼Åµµ ÁÁ½À´Ï´Ù.

Github¿¡¼­ ÀüüÇÁ·ÎÁ§Æ® º¸±â


(³»¿ëÀÌ À¯¿ëÇϼ̴ٸé Github ¿À¸¥ÂÊÀ§ÀÇ [Star]¹öÆ°À» ´­·¯ÁÖ½Ã¸é °¨»çÇÏ°Ú½À´Ï´Ù

Àú¿¡°Ô´Â º°Ç³¼± Áֽô ȿ°ú°¡ ÀÖ¾î¿ä!)



Ãâó: https://gun0912.tistory.com/46 [¹Ú»ó±ÇÀÇ »ðÁúºí·Î±×]