단말기의 모뎀상태에 대한 정보를 얻기 위해서는 READ_PHONE_STATE권한이 필요하다.
AndroidManifest.xml파일에 아래 내용을 추가한다.
<uses-permission 
android:name="android.permission.READ_PHONE_STATE">
</uses-permission>

◆ 단말기의 모뎀상태 조회
TelephonyManager 객체를 얻기 위해서는 Context 객체에서 제공하는 getSystemService() 메서드를 이용한다.
TelephonyManager tm = (TelephonyManager) 
getSystemService(TELEPHONY_SERVICE);

음성통화 상태 조회
CALL_STATE_IDLE/CALL_STATE_OFFHOOK/CALL_STATE_RINGING 등의 값을 반환
Log.d("PHONE", "getCallState :" + tm.getCallState());
데이터통신 상태 조회
DATA_DISCONNECTED/DATA_CONNECTING/DATA_CONNECTED/DATA_SUSPENDED 등의 값을 반환
Log.d("PHONE", "getDataState :" + tm.getDataState());
단말기 ID 조회
GSM방식 IMEI 또는 CDMA방식의 MEID 값을 반환
Log.d("PHONE", "getDeviceId :" + tm.getDeviceId());
SW버전 조회
GSM방식의 IMEI/SV와 같은 SW버전을 반환
Log.d("PHONE", "getDeviceSoftwareVersion :" + tm.getDeviceSoftwareVersion());
전화번호 조회
GSM방식의 MSISDN과 같은 전화번호 반환
Log.d("PHONE", "getLine1Number :" + tm.getLine1Number());
국가코드 조회
현재 등록된 망 사업자의 MCC(Mobile Country Code)에 대한 ISO 국가코드 반환
Log.d("PHONE", "getNETWORKCountryIso :" + tm.getNetworkCountryIso());
Log.d("PHONE", "getSimCountryIso :" + tm.getSimCountryIso());
망 사업자코드 조회
현재 등록된 망 사업자의 MCC+MNC(Mobile Network Code) 반환
Log.d("PHONE", "getNetworkOperator :" + tm.getNetworkOperator());
Log.d("PHONE", "getSimOperator :" + tm.getSimOperator());
망 사업자명 조회
현재 등록된 망 사업자명 반환
Log.d("PHONE", "getNetworkOperatorName :" + tm.getNetworkOperatorName());
Log.d("PHONE", "getSimOperatorName :" + tm.getSimOperatorName());
망 시스템 방식 조회
현재 단말기에서 사용중인 망 시스템 방식을 반환
NETWORK_TYPE_UNKNOWN/
GSM방식 :  NETWORK_TYPE_GPRS/NETWORK_TYPE_EDGE/NETWORK_TYPE_UMTS/
NETWORK_TYPE_HSDPA/NETWORK_TYPE_HSUPA/NETWORK_TYPE_HSPA
CDMA방식 : NETWORK_TYPE_CDMA/NETWORK_TYPE_EVDO_0/NETWORK_TYPE_EVDO_A/NETWORK_TYPE_1xRTT
Log.d("PHONE", "getNetworkType :" + tm.getNetworkType());
단말기 종류 조회
단말기에서 지원하는 망의 종류를 반환
PHONE_TYPE_NONE/PHONE_TYPE_GSM/PHONE_TYPE_CDMA 등의 값을 반환
Log.d("PHONE", "getPhoneType :" + tm.getPhoneType());
SIM카드 Serial Number 조회
Log.d("PHONE", "getSimSerialNumber :" + tm.getSimSerialNumber());
SIM카드 상태 조회
SIM_STATE_UNKNOWN/SIM_STATE_ABSENT/SIM_STATE_PIN_REQUIRED/SIM_STATE_PUK_REQUIRED/
SIM_STATE_NETWORK_LOCKED/SIM_STATE_READY 등의 값을 반환
Log.d("PHONE", "getSimState :" + tm.getSimState());
가입자 ID 조회
GSM방식의 IMSI와 같은 가입자 ID 반환
Log.d("PHONE", "getSubscriberId :" + tm.getSubscriberId());

◆ 조회결과
getCallState :0(CALL_STATE_IDLE)
getDataState :2(DATA_ACTIVITY_OUT)
getDeviceId :000000000000000
getDeviceSoftwareVersion :null
getLine1Number :15555218135
getNetworkCountryIso :us
getNetworkOperator :310260
getNetworkOperatorName :Android
getNetworkType :3(NETWORK_TYPE_UMTS)
getPhoneType :1(PHONE_TYPE_GSM)
getSimCountryIso :us
getSimOperator :310260
getSimOperatorName :Android
getSimSerialNumber :89014103211118510720
getSimState :5(SIM_STATE_READY)
getSubscriberId :310260000000000

실제상황에서는 이와같이 현재 단말기의 상태를 직접 조회하는것이 아니라 
상태정보가 변경될 때 자동으로 인식해야하는 상황이 훨씬 많을것이다.
이럴때는 TelephonyManager의 listen()를 이용하여 콜백메서드를 등록하면 가능하다.
        tm.listen(new PhoneStateListener(){
         public void onCallStateChanged(int state, String incomingNumber){
         if (state == TelephonyManager.CALL_STATE_RINGING){
         Log.d("Telephony", "state = " + state + ", number = " + incomingNumber);
         }else{
         Log.d("Telephony", "state = " + state);
         }        
         }
        }, PhoneStateListener.LISTEN_CALL_STATE);

onCallStateChanged() 이외에도 아래와 같은 상태변화를 감지할 수 있다.
- onCallForwardingIndicatorChanged() : 호전환(Call Forwarding) 상태 변화
- onCellLocationChanged() : 단말기의 Cell위치 변화
- onDataActivity() : Data 활성화 상태 변화
- onDataConnectionStateChanged() : Data 연결상태 변화
- onMessageWaitingIndicatorChanged() : 메시지 대기상태 변화
- onServiceStateChanged() : 단말기의 서비스 상태 변화
- onSignalStrengthsChanged() : 망의 신호강도 변화

◆ 전화번호 처리
각 국가별로 전화번호의 형식이 다르다. 
한국은 01x-xxxx-xxxx 이 일반적이고,
북미는 xxx-xxx-xxxx가 된다.
PhoneNumberUtils.formatNumber() 메서드를 사용하면 설정된 Locale에 맞게 형식화 된다.
String formattedTelNumber = PhoneNumberUtils.formatNumber("01098761234");
        Log.d("Telephony", "formattedTelNumber :" + formattedTelNumber);
Locale을 English(United States)로 할경우 010-987-61234로 출력된다.
한국어로 할 경우는 01098761234가 그대로 출력된다.
formatNumber() 메서드의 구현내용을 살펴봐야 할듯 하다.

EditText에서 전화번호를 입력받을 경우에도 자동으로 형식화가 가능하다.
EditText객체의 addTextChangedListener에 PhoneNumberFormattingTextWatcher()를 등록하기만 하면 된다.
        EditText tn = (EditText) findViewById(R.id.edtTelNumber);
        tn.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
그러나 역시 한국어로는 동작하지 않았다.


 public boolean onTouchEvent(final MotionEvent event)
 
{
     
final int action = event.getAction();
     
if(mIsMultiTouchSupported)
     
{
     
switch (action & MotionEvent.ACTION_MASK)
       
{
       
case  MotionEvent.ACTION_POINTER_DOWN : {
     
if(mActivePointerId == -1)
     
{
             
final int pind = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
             
int pid = event.getPointerId(pind);
             
final int x = (int)event.getX(pid);
             
final int y = (int)event.getY(pid);
             
             
if(pointInArea(x, y))
             
{
              mActivePointerId
= pid;
             
               
// System.out.println("down pointer x = "+x+" , y = "+y);
                onAction
(UIAREA_ACTION_DOWN, x, y);
             
}
     
}
       
}
         
break;
       
case MotionEvent.ACTION_DOWN: {
         
if(mActivePointerId == -1)
         
{
             
final int x = (int)event.getX();
             
final int y = (int)event.getY();
             
if(pointInArea(x, y))
             
{
              mActivePointerId
= event.getPointerId(0);
             
// System.out.println("down x = "+x+" , y = "+y);
              onAction
(UIAREA_ACTION_DOWN, x, y);
             
}
         
}
       
           
break;
       
}
           
       
case MotionEvent.ACTION_MOVE: {
         
if(mActivePointerId != -1)
         
{
             
// Find the index of the active pointer and fetch its position
             
final int pointerIndex = event.findPointerIndex(mActivePointerId);
             
final int x = (int)event.getX(pointerIndex);
             
final int y = (int)event.getY(pointerIndex);
             onAction
(UIAREA_ACTION_MOVE, x, y);
         
}
 
           
break;
       
}
           
       
case MotionEvent.ACTION_CANCEL: {
            mActivePointerId
= -1;
           
break;
       
}
       
case MotionEvent.ACTION_UP:
       
case MotionEvent.ACTION_POINTER_UP: {
           
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
           
final int pointerId = event.getPointerId(pointerIndex);
           
if (pointerId == mActivePointerId) {

             
final int x = (int)event.getX(mActivePointerId);
             
final int y = (int)event.getY(mActivePointerId);
                mActivePointerId
= -1;
               
// System.out.println(" up x = "+x+" , y = "+y);
                onAction
(UIAREA_ACTION_UP, x, y);
           
}
           
break;
       
}
       
}//end of switch      
     
}
     
else
     
{
     
switch (action)
     
{
       
case MotionEvent.ACTION_DOWN: {
           
final int x = (int)event.getX();
           
final int y = (int)event.getY();
           
if(pointInArea(x, y))
           
{
           
// System.out.println("down x = "+x+" , y = "+y);
             onAction
(UIAREA_ACTION_DOWN, x, y);
           
}
           
break;
       
}
           
       
case MotionEvent.ACTION_MOVE: {
           
final int x = (int)event.getX();
           
final int y = (int)event.getY();
           
if(pointInArea(x, y))
           
{
           
// System.out.println("move x = "+x+" , y = "+y);
             onAction
(UIAREA_ACTION_MOVE, x, y);
           
}
           
break;
       
}
           
       
case MotionEvent.ACTION_CANCEL: {
           
break;
       
}
       
case MotionEvent.ACTION_UP:{
           
final int x = (int)event.getX();
           
final int y = (int)event.getY();
               onAction
(UIAREA_ACTION_UP, x, y);
           
break;
       
}
     
}//end of switch
     
}    
   
return true;

 
}

spring error stacktrace 에러 내용이 찍히지 않을때

[2009-04-25 10:21:11,197](441)  Trying to find handler for exception class[java.lang.Exception]

 

에러가 발생하게 되면 보통 stacktrace 의 객체에 저장된 내용을 print 해서 문제를 파악하게 된다.

하지만 spring framework 에서 위와 같이 간단하게 표현되는 경우가 있다.

그 원인을 찾기 위해서 Controller 가 상속받은 클래스를 따라 올라가다 보니

MultiActionController 함수중에 Exception  이 발생할때 실행되는 놈이 있는데 그 함수가

아래와 같다.

 

protected Method getExceptionHandler(Throwable exception){

Class exceptionClass = exception.getClass();

if(logger.isDebugEnabled())

logger.debug("Trying to find handler for exception class [" + exceptionClass.getName() + "]");

       Method handler;

       for(handler = (Method)exceptionHandlerMap.get(exceptionClass); 
handler == 
null && !exceptionClass.equals(java.lang.Throwable.class); 
handler = (Method)
exceptionHandlerMap.get(exceptionClass))

        {

            if(logger.isDebugEnabled())

                logger.debug("Trying to find handler for exception superclass [" + exceptionClass.getName() + "]");

            exceptionClass = exceptionClass.getSuperclass();

   }

 

   return handler;

}

내용을 보면 대충 알겠지만 logger.isDebugEnabled 일때 Trying to find handler  print하는걸 볼수 있다. 
 옵션을 어떻게 바꿔야 될지  찾아봐야겠지만 
당장은 MultiActionController 상속받아 
클래스를 만든후 getExceptionHandler 오버로딩 
해서 기능을 변경하는것이 편할것 같다.

방법은 exception 객체에서 StackTraceElement[] 배열을 가져온후 그내용을 StringBuffer

저장하여 log4j  출력하는 절차이다. 문자열을 구성한 로직은 일반적인 에러 출력형태를

취해서 print 한다. 다음은 오버로딩한 함수의 내용이다.

 

protected Method getExceptionHandler(Throwable exception){

   StackTraceElement[] element = exception.getStackTrace();

   StringBuffer errorBuff = new StringBuffer(exception.getMessage());

   errorBuff.append("\n");

   for (int i=0; i < element.length; i++){

      errorBuff.append("\tat ").append(element[i]).append("\n");

   }

   log.debug(errorBuff.toString());

   return super.getExceptionHandler(exception);

}


출처 : http://mainia.tistory.com/335

'안드로이드' 카테고리의 다른 글

Telephony 기능  (0) 2011.01.27
멀티 터치 구현 예제  (0) 2011.01.27
java.lang.Annotation(주석:@)  (0) 2011.01.27
View Fipper  (0) 2011.01.27
상태바(Status Bar)  (0) 2011.01.27

# 이 문서는 오라클클럽에서 작성하였습니다. 
# 이 문서를 다른 블로그나 홈페이지에 게재하실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^ 
# 출처 : http://wiki.oracleclub.com/display/WEBSTUDY/Annotations? 

Annotation(주석)

1. Annotation(주석) 개요

J2SE 5.0에 추가된 중요한 기능중 하나로, 어노테이션 유형은 일반 클래스와 비슷해보이지만 독특한 속성이 있다.
표기법은 (@)(at) 표시로 하고, 클래스에서 @(at)기호와 함께 사용하여 다른 자바 코드에 주석을 달 수 있다.
주석태그가 코멘트가 아니라 소스에 포함된다. 여기서 중요한 점은 주석이 선언적 프로그래밍 모델을 체계적으로 지원하기 위한 방법이라는 것이다.

1.1. 주석이란

  • 메타데이터를 프로그램 엘리먼트(클래스, 인터페이스, 메소드 등)에 연결하는 방법을 제시합니다.
    (메타데이터는 데이터를 부연설명하기 위한 데이터)
  • 주석은 해당 엘리먼트에 대해 생성된 바이트코드를 변경하지 않는 추가 수식자(modifier)라고 할 수 있다.

1.2. 주석의 지속성

  • //, /* */, /** */ 등의 주석은 보통 컴파일이 되면서 사라진다.
  • Annotation은 기존의 주석과 달리 Runtime 까지도 존재하는 주석이다.

2. Builtln(내장된) Annotation(주석)

  • 모든 자바 프로그래머들이 알아야 하는 내장 어노테이션은 @Deprecated, @Override, @SuppressWarnings 모두 3가지 이다.

2.1. Override Annotation

  • Tiger의 첫 번째 Builtln(내장된) Annotation 유형이다.
  • @Override는 메소드에 대해서만 사용되어야 한다. (클래스, 패키지 선언, 기타 구조체는 안된다.)
  • @Override 주석이 사용된 메서드는 수퍼클래스에서 메소드를 오버라이드한다는 것을 나타낸다.

2.1.1. @Override 예제

The Override Annotation

package dfi.study.java;

public class OverrideTest01{

public OverrideTest01() { }

@Override
public String toString() {
return super.toString() + " [Override Test]";
}

@Override
public int hasCode() {
return toString().hashCode();
}
}
  • 위의 @Override 어노테이션은 두 개의 메소드, toString()과 hashCode()가 OverrideTest01 클래스의 수퍼클래스 (java.lang.Object)에서
    메소드의 버전을 오버라이드 한다는 것을 나타내고 있다.
  • 메소드를 오버라이드 하려다가 메소드 이름의 철자를 틀리거나, 잘못된 인자를 지정하거나, 다른 리턴 타입을 설정했던 적이 무수히 많았을 것이다.
    이 어노테이션 유형은 코딩하기엔 너무 늦었거나 무언가를 잘못 타이핑했을 때 빛을 발한다.

Override Annotation의 오타 찾아내기

  • 위 코드에서 hashCode()가 hasCode()로 잘못 표기되었다. @Override 어노테이션은 hasCode()가 메소드를 오버라이드해야 한다는 것을 지시한다.
    하지만 컴파일시 오버라이드 할 hasCode()라는 메소드가 없다는 것을 알게 되고. 결과적으로 컴파일러는 에러를 표시한다.

2.2. Deprecated Annotation

  • @Override와 마찬가지로 @Deprecated는 marker 어노테이션이다.
  • @Deprecated를 사용하여 더 이상 사용되지 말아야 하는 메소드에 주석을 단다. 주의할 점은 더 이상 사용되지 말아야 하는(depreciated) 메소드와
    같은 라인상에 놓여져야 한다는 것이다.
  • 컴파일러는 프로그램이 비추천(deprecated) 메소드나 클래스 혹은 변수를 사용할 때마다 경고를 발생시킨다.
  • 메소드에 @Deprecated 태그를 플래그하면 해당 메소드나 클래스 사용시 사용자에게 경고 메세지를 보내도록 컴파일러를 환기시키는 효과가 있다.
  • 태그는 소문자 'd'로 시작하며, Annotation은 대문자' D'로 시작함을 유의해야한다.
  • 일반적으로 프로그래머는 비추천 메소드의 사용을 피해야 하며 그것 대신 무엇을 사용해야 하는지를 확인해야 한다.

2.2.1. @Deprecated 예제

The Deprecated Annotation

  • 주석이 붙은 클래스도 주석이 붙지 않은 경우와 동일한 방식으로 컴파일한다.
  • 이 클래스를 컴파일 할 때 비정상적인 그 어떤 것도 기대해서는 안된다.
  • 오버라이드 또는 호출이든 @Depreciated 메소드를 사용하면 컴파일러는 어노테이션을 처리하고
    메소드가 사용되어서는 안된다는 것을 알게 되고 에러 메시지를 만든다.

2.3. SuppressWarnings Annotation

  • 마지막 어노테이션 유형은 SuppressWarnings으로 가장 흥미롭다고 할 수 있다.
  • @SuppressWarnings은 일반적으로 경고하는 내용을 경고하지 말도록 컴파일러에게 지시하는데, 경고는 일종의 범주에 속하므로 주석에 대해
    어떤 종류의 경고를 금지할 것인지 지시해야 한다.
  • Override와 Deprecated와는 다르게 SupressWarnings는 변수를 갖고 있다. 따라서 이를 작동하게 하려면 싱글-어노테이션 유형을 사용해야한다.
  • javac 컴파일러는 all, deprecation, unchecked, fallthrough, path, serial, finally 등 7개의 금지 옵션을 정의한다.
  • 언어 스펙은 이 중에서 두 가지 비추천(deprecation)"과 "비확인(unchecked) 만을 정의한다.
  • 하나 이상의 경고 종류를 억제하려면 다음의 문법과 같이 사용하면 된다.
     @SuppressWarnings({"unchecked", "deprecation"})

2.3.1. @SuppressWarnings 예제

The SuppressWarnings Annotation (type-safe가 아닌 Tiger 코드)

package dfi.study.java;
import java.util.ArrayList;

public class SuppressWarningsTest01
{
public void SuppressWarningsTest01()
{
ArrayList wordList = new ArrayList(); // no typing information on the List

wordList.add("study"); // causes error on list addition
}
}

non-typed 코드에서 컴파일러 경고

-컴파일 라인에 -Xlint를 추가하면 무엇이 잘못되었는지 구체적으로 표시할 수 있다.

경고 제거하기

  • SuppressWarnings 어노테이션을 사용하여 번거로운 경고를 제거한다
    -@SuppressWarnings(value={"unchecked"})를 적용함.
package dfi.study.java;
import java.util.ArrayList;

@SuppressWarnings(value={"unchecked"})
public class SuppressWarningsTest01
{
public void SuppressWarningsTest01()
{
ArrayList wordList = new ArrayList(); // no typing information on the List

wordList.add("study"); // causes error on list addition
}
}


# 이 문서는 오라클클럽에서 작성하였습니다. 
# 이 문서를 다른 블로그나 홈페이지에 게재하실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^ 
# 출처 : http://wiki.oracleclub.com/display/WEBSTUDY/Annotations?

'안드로이드' 카테고리의 다른 글

멀티 터치 구현 예제  (0) 2011.01.27
spring framework 적용하여 controller 에서 error stacktrace 에러 내용이 찍히지 않을때  (0) 2011.01.27
View Fipper  (0) 2011.01.27
상태바(Status Bar)  (0) 2011.01.27
PowerManager  (0) 2011.01.27

ViewFlipper

 : In/Out 속성을 지정하여 View 의 이동을 animation 처리한다.


1. 레이아웃 구성
  ViewFlipper 에 포함될 View 를 정의

2. 애니메니션 구성
  res/anim 에 원하는 애니메이션 구성

3. 코드 구성
mFilpper = (ViewFlipper)this.findVeiwById(R.id.flipper)
mFilpper.startFlipping();
mFilpper.setInAnimation(...);
mFilpper.setOutAnimation(...);


* 선언한 뷰를 class로 매핑
    flipper = (ViewFlipper)findViewById(R.id.flipper);
    title = (Title)flipper.getChildAt(0);
    menu = (Menu)flipper.getChildAt(1);
    game = (Game)flipper.getChildAt(2);

* 원하는 뷰를 화면에 나타내기
    flipper.setDisplayedChild(2);

* 참고사항

setContentView혹은 layoutinflate를 하는 시점에 모든 뷰가 생성이 되고 화면에 보이는 여부와 상관없이

종료할때까지는 계속 살아 있기 때문에 특정 view를 보일때 초기화를 한다든지 하는건 알아서 해줘야함

(별도의 overriding할 method같은건 당연히 제공되지 않음)

 

## 이벤트 처리
http://tigerwoods.tistory.com/23

OnTouchListener 인터페이스의 구현

화면에 일어나는 touch를 감지하기 위해서는 android.view.View.OnTouchListener interface를 구현하여야 한다.

OnTouchListener 인터페이스는 다음과 같은 abstract callback method를 포함 함으로 구현해 주어야 한다.


Animation 리소스의 사용 
  push_left_in.xml: 새로운 view가 왼쪽 방향으로 밀려 들어옴. 
  push_left_out.xml: 기존 view가 왼쪽 방향으로 밀려 나감. 
  push_right_in.xml: 새로운 view가 오른쪽 방향으로 밀려 들어옴. 
  push_right_out.xml: 기존 view가 오른쪽 방향으로 밀려 나감.


<translate> 
상하좌우 이동 animation을 지정하며. TranslateAnimation 클래스에 대응한다.

<alpha> 
투명함을 조정하며, AlphaAnimation 클래스에 대응한다.


Java code에서 ViewFlipper 객체의 사용
  View:addView(View): layout xml 문서 내부의 ViewFlipper element에 nest된 view 이외에 새로운 view를 동적으로 추가한다. 
  ViewAnimator:setInAnimation(Context, int): 새로운 view가 화면에 진입시의 animation 설정 
  ViewAnimator:setOutAnimation(Context, int): 기존 view가 화면에서 퇴장시의 animation 설정 
  ViewAnimator:showNext(): ViewFlipper에 등록된 view중 현재 view의 다음 view를 화면에 보이게 함
  ViewAnimator:showPrevious(): ViewFlipper에 등록된 view중 현재 view의 전 view를 화면에 보이게 함 
  ViewFlipper:setFlipInterval(int): 자동 flipping시 적용할 시간간격 (1/1000초 단위 사용)
  ViewFlipper:startFlipping(): 설정된 시간간격을 적용해 자동 flipping 시작 함 
  ViewFlipper:stopFlipping(): 자동 flipping을 정지 함


ViewSwitcher : ViewFlipper와 동일

 

Key classes

  1. Notification
  2. NotificationManager

In this document

  1. The Basics
  2. Managing your Notifications
  3. Creating a Notification
    1. Updating the notification
    2. Adding a sound
    3. Adding vibration
    4. Adding flashing lights
    5. More features
  4. Creating a Custom Expanded View

상태바Status Bar 노티피케이션은 시스템의 상태바(선택 사항으로 조회 텍스트(ticker-text) 메시지를 가짐)에 아이콘을, 그리고 “노티피케이션notifications” 윈도우에는 확장 메시지를 추가한다. 그 사용자가 확장 메시지를 선택할 때, 안드로이드는 노티피케이션에 정의된 인텐트를 발생시킨다(보통 액티비티를 런치하기 위해). 여러분은 또한 디바이스 상의 사운드sound, 진동vibration, 그리고 발광 빛flashing light을 사용해서 사용자에게 경보alert하도록 노티피케이션을 설정할 수 있다.

상태바 노티피케이션은 백그라운드 서비스Service가 사용자에게 응답을 요구하는 이벤트를 경보alert 하는 것이 필요한 경우에 사용되어야 한다. 백그라운드 서비스는 사용자와의 상호작용을 받아들이기 위해 그것 자신의 액티비티를 결코 런치해서는 안된다. 서비스는 대신 사용자에 의해 선택될 때, 액티비티를 런치할 상태바 노티피케이션을 생성해야 한다.

아래의 스크린샷은 왼쪽 사이드에 노티피케이션 아이콘을 가진 상태바를 보여준다.

아래의 스크린샷은 “노티피케이션Notifications” 윈도우에 있는 노티피케이션의 확장 메시지를 보여준다. 사용자는 상태바를 아래로 내림(또는 홈 옵션 메뉴에서 노티피케이션을 선택함)으로써 노티피케이션 윈도우를 나타나게 할 수 있다.

기초(The Basics)

액티비티 또는 서비스는 상태바 노티피케이션을 시작시킬 수 있다. 액티비티는 그것이 활성화되어 있고 포커스되어 있는 동안에만 액션을 수행할 수 있기 때문에, 여러분은 서비스로부터 여러분의 상태바 노티피케이션을 생성해야 한다. 이 방식에서, 노티피케이션은 사용자가 다른 애플리케이션을 사용하거나 또는 그 디바이스가 절전상태에 있는 동안 백그라운드로부터 생성될 수 있다. 노티피케이션을 생성하기 위해서, 여러분은 두 가지 클래스를 사용해야 한다: Notification과 NotifactionManager가 그것이다.

상태바status bar 아이콘, 확장 메시지expanded message, 그리고 재생할 사운드와 같은 별도의 설정과 같이, 여러분의 상태 바 노티피케이션의 속성들을 정의하기 위해서는 노티피케이션Notification 클래스의 인스턴스를 사용하라. 노티피케이션 매니저NotificationManager는 모든 노티피케이션을 실행하고 관리하는 안드로이드 시스템 서비스이다. 여러분은 노티피케이션 매니저를 인스턴스화하지는 않는다. 여러분의 노티피케이션을 노티피케이션 매니저에게 건네주기 위해, 여러분은 getSystemService()를 사용하여 노티피케이션 매니저에 대한 레퍼런스를 얻어와야 한다. 그런 다음에 여러분이 사용자에게 통보하길 원할 때, notify()를 사용해서 노티피케이션 매니저에게 여러분의 노티피케이션 오브젝트를 전달하라.

상태바 노티피케이션을 생성하기 위해서는 다음과 같이 하라.

  1. 노티피케이션 매니저에 대한 레퍼런스를 얻어라.
    String ns = Context.NOTIFICATION_SERVICE;
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
    
  2. 노티피케이션을 인스턴스화하라.
    int icon = R.drawable.notification_icon;
    CharSequence tickerText = "Hello";
    long when = System.currentTimeMillis();
    
    Notification notification = new Notification(icon, tickerText, when);
    
  3. 노티피케이션의 확장 메시지와 인텐트를 정의하라.
    Context context = getApplicationContext();
    CharSequence contentTitle = "My notification";
    CharSequence contentText = "Hello World!";
    Intent notificationIntent = new Intent(this, MyClass.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    
    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
    
  4. 노티피케이션 매니저에게 노티피케이션을 전달하라.
    private static final int HELLO_ID = 1;
    
    mNotificationManager.notify(HELLO_ID, notification);
    

    이것이 끝이다. 여러분의 사용자는 이제 노티피케이션을 통보받았다notified.

노티피케이션 관리하기(Managing your Notifications)

노티피케이션 매니저NotificationManager는 모든 노티피케이션을 관리하는 시스템 서비스이다. 여러분은 getSystemService()를 사용해서 노티피케이션 매니저에 대한 레퍼런스를 얻어와야 한다. 예를 들어 다음과 같다.

String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);

여러분이 상태바 노티피케이션을 전송하고자 할 때, 노티피케이션 매니저에게notify(int,Notification)를 사용해서 노티피케이션 오브젝트를 파라미터로 전달하라. 첫 번째 파라미터는 노티피케이션에 대한 고유한 ID이며, 두 번째는 노티피케이션 오브젝트이다. 그 ID는 여러분의 애플리케이션으로부터 그 노티피케이션을 고유하게 식별한다. 이 ID는 여러분이 노티피케이션을 업데이트하는 것이 필요하거나 또는 (여러분의 애플리케이션이 다른 종류의 노티피케이션들을 관리한다면) 사용자가 노티피케이션에 정의된 인텐트를 통해 여러분의 애플리케이션으로 돌아올 때 적절한 액션을 선택하는 것이 필요할 때 필수적이다.

사용자가 노티피케이션 윈도우에서 상태바 노티피케이션을 선택할 때 그것을 없애기 위해서는, 여러분의 노티피케이션 오브젝트에 “FLAG_AUTO_CANCEL” 플래그를 추가하라. 여러분은 또한 cancel(int)를 사용해서 상태바 노티피케이션에 노티피케이션 ID를 전달함으로써 수동으로 그것을 없앨 수 있으며, 또한 cancelAll()를 사용해서 여러분의 모든 노티피케이션을 없앨 수 있다.

노티피케이션 생성(Creating a Notification)

노티피케이션Notificaton 오브젝트는 상태바에 표시되는 노티피케이션 메시지의 상세 사항과 “노티피케이션Notifcations” 윈도우, 그리고 사운드와 깜박이는 발광 빛과 같은 그 밖의 경보 설정을 정의한다.

상태바 노티피케이션은 다음과 같은 모든 것을 요구한다.

  • 상태 바에 대한 아이콘.
  • 확장 뷰에 대한 타이틀과 확장 메시지(만약 여러분이 커스텀 확장 뷰를 정의하지 않는다면).
  • 노티피케이션이 선택될 때 일어나도록 하기 위한 PendingIntent.

상태바 노티피케이션에 대한 선택적인 설정에는 다음과 같은 것이 포함된다.

  • 상태바에 대한 조회 텍스트(ticker-text) 메시지
  • 경보 사운드
  • 진동 설정
  • 발광 LED 설정

신규 노티피케이션에 대한 시작 도구는 Notification(int,CharSequence,long) 생성자와 setLatestEventInfo(Context,CharSequence,CharSequence, PendingIntent) 메쏘드를 포함한다. 이것들은 노티피케이션에 대해 요구되는 모든 설정을 정의한다. 아래의 단편snippet은 기본적인 노티피케이션 셋업을 보여준다.

int icon = R.drawable.notification_icon;        // icon from resources
CharSequence tickerText = "Hello";              // ticker-text
long when = System.currentTimeMillis();         // notification time
Context context = getApplicationContext();      // application Context
CharSequence contentTitle = "My notification";  // expanded message title
CharSequence contentText = "Hello World!";      // expanded message text

Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

// the next two lines initialize the Notification, using the configurations above
Notification notification = new Notification(icon, tickerText, when);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

노티피케이션 업데이트하기(Updating the notification)

이벤트들이 여러분의 애플리케이션에서 계속 발생함에 따라 여러분은 상태바 노티피케이션에 있는 정보를 업데이트할 수 있다. 예를 들어 이전 메시지가 읽혀지기 전에 신규 SMS 텍스트 메시지가 도착하면, 그 메시징 애플리케이션은 수신된 신규 메시지의 전체 숫자를 보여주기 위해 이미 존재하는 노티피케이션을 업데이트 한다. 이미 존재하는 노티피케이션을 업데이트하는 이 방식은, 노티피케이션 윈도우에서의 혼란clutter을 피하기 때문에 노티피케이션 매니저에 신규 노티피케이션을 추가하는 것보다 훨씬 더 좋다.

각각의 노티피케이션은 정수integer ID를 가지고 노티피케이션 매니저에 의해 고유하게 식별되기 때문에, 여러분은 신규 값을 가지고 setLatestEventInfo()를 호출함으로써 노티피케이션을 수정하고, 그 노티피케이션의 일부 필드 값들을 변경하고, 그리고 나서 다시 notify()를 호출할 수 있다.

여러분은 (컨텍스트와 확장 메시지 타이틀과 텍스트를 제외하고) 오브젝트 멤버 필드를 사용해서 각각의 속성을 수정할 수 있다. contentTitle과 contentText에 대한 신규 값을 가지고 setLatestEventInfo()를 호출함으로써 여러분이 그 노티피케이션을 업데이트할 때, 여러분은 항상 그 텍스트 메시지를 변경revise해야 한다. 그런 다음 그 노티피케이션을 업데이트 하기 위해 notify()를 호출하라(물론, 만약 여러분이 커스텀 확장 뷰를 생성했다면, 이러한 타이틀과 텍스트 값을 업데이트하는 것은 아무런 영향을 주지 않는다).

사운드 추가하기(Adding a sound)

여러분은 디폴트 노티피케이션 사운드(이것은 사용자에 의해 정의된다) 또는 여러분의 애플리케이션에 의해 지정된 사운드를 사용해서 사용자에게 경보alert할 수 있다.

사용자의 디폴트 사운드를 사용하기 위해서는, 디폴트 필드에 “DEFAULT_SOUND”를 추가하라.

notification.defaults |= Notification.DEFAULT_SOUND;

여러분의 노티피케이션에서 다른 사운드를 사용하기 위해서는, 사운드 필드에 Uri 레퍼런스를 전달하라. 다음의 예제는 디바이스 SD 카드에 저장된 알고 있는 오디오 파일을 사용한다.

notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");

다음의 예제에서는, 그 오디오 파일이 내부 MediaStore 컨텐트 프로바이더에서 선택된다.

notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");

이 경우에, 미디어 파일 (“6”)에 대한 정확한 ID를 알고 있으며, 그리고 컨텐트 Uri의 끝에 덧붙여진다. 만약 여러분이 정확한 ID를 모른다면, 여러분은 컨텐트 리졸버ContentResolver를 사용해서 미디어스토어MediaStore에서 사용가능한 모든 미디어를 쿼리해야 한다. 컨텐트 리졸버ContentResolver를 사용하는 것에 대한 더 자세한 정보에 대해서는 7장. “컨텐트 프로바이더”를 보라.

만약 여러분이 사용자가 노티피케이션에 응답할 때까지 또는 노티피케이션이 취소될 때까지 사운드가 계속해서 반복되길 원한다면, 그 플래그 필드에 “FLAG_INSISTENT”를 추가하라.

Note: 만약 그 디폴트 필드가 “DEFAULT_SOUND” 를 포함한다면, 그러면 그 디폴트 사운드는 사운드 필드에 의해 정의된 임의의 사운드를 오버라이드 한다.

진동 추가하기(Adding vibration)

여러분은 디폴트 진동 패턴 또는 여러분의 애플리케이션에 의해 정의된 진동 패턴을 사용해서 사용자에게 경보alert할 수 있다.

디폴트 패턴을 사용하기 위해서는, 디폴트 필드에 “DEFAULT_VIBRATE”를 추가하라.

notification.defaults |= Notification.DEFAULT_VIBRATE;

여러분 자신의 진동 패턴을 정의하기 위해서는 vibrate 필드에 long 값 배열을 전달하라.

long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;

long 배열은 진동이 꺼지고 켜지는 길이에 대한 번갈아 있는 패턴을 (1/1000초 단위로) 정의한다. 첫 번째 값은 시작하기 전에 얼마나 오래 기다릴 것인가(꺼진 상태)이고, 두 번째 값은 첫 번째 진동의 길이이고, 세 번째는 그 다음의 꺼져 있는 길이, 그리고 기타 등등이다. 그 패턴은 여러분이 좋아하는 만큼 길 수 있으나 반복되도록 설정될 수 없다.

Note: 만약 디폴트 필드가 “DEFAULT_VIBRATE”를 포함한다면, 그러면 그 디폴트 진동은 vibrate 필드에 의해 정의된 임의의 진동을 오버라이드 한다.

발광 빛 추가하기(Adding flashing lights)

발광 LED 빛을 사용해서 사용자에게 경보alert하기 위해서, 여러분은 (만약 사용할 수 있다면) 디폴트 빛 패턴을 구현할 수 있거나 또는 여러분 자신의 컬러와 그 빛에 대한 패턴을 정의할 수 있다.

디폴트 빛을 설정하기 위해서는 그 디폴트 필드에 “DEFAULT_LIGHTS”를 추가하라.

notification.defaults |= Notification.DEFAULT_LIGHTS;

여러분 자신의 컬러와 패턴을 정의하기 위해서는 (컬러에 대해서는) ledARGB 필드, ledOffMS 필드(1/1000초 단위의 빛이 꺼져 유지되는 시간의 길이), ledOnMS(1/1000초 단위의 빛이 켜져 유지되는 시간의 길이) 값을 정의하고, 그리고 또한 그 플래그 필드에 “FLAG_SHOW_LIGHTS”를 추가하라.

notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;

이 예제에서, 초록색 빛이 반복적으로 300 밀리세컨드(1/1000초) 동안 켜지고 1초 동안 꺼진다. 스펙트럼 내의 모든 컬러가 디바이스 LED에 의해 지원되는 것은 아니다. 그리고 모든 디바이스가 동일 컬러를 지원하지 않는다. 그러므로 그 하드웨어는 자신의 능력의 최선을 산정한다. 초록색은 가장 일반적인 노티피케이션 컬러이다.

더 많은 기능(More features)

여러분은 노티피케이션 필드와 플래그를 사용해서 여러분의 노티피케이션에 더 많은 몇가지 기능들을 추가할 수 있다. 약간의 유용한 기능들이 아래에 포함되어 있다.

"FLAG_AUTO_CANCEL" flag
노티피케이션 윈도우에서 노티피케이션이 선택된 이후, 그 노티피케이션이 자동으로 취소되게끔 하기 위해서는 이 플래그를 flags 필드에 추가하라.
"FLAG_INSISTENT" flag
사용자가 응답할 때까지 오디오를 반복시키기 위해서는 이 플래그를 flags 필드에 추가하라.
"FLAG_ONGOING_EVENT" flag
노티피케이션 윈도우의 “Ongoing” 타이틀 아래의 노티피케이션을 그룹화하기 위해서는 이것을 그 플래그에 추가하라. 이것은 애플리케이션이 진행중on-going이라는 것을 가리킨다 ? (뮤직이나 전화 호출과 같이) 심지어 애플리케이션이 보이지 않을 때 조차도, 그 애플리케이션 프로세스는 여전히 백그라운드에서 실행중이다.
"FLAG_NO_CLEAR" flag
노티피케이션이 “Clear” 노티피케이션 버튼에 의해서 없어져서는 안된다는 것을 가리키기 위해서는 이 플래그를 flags 필드에 추가하라. 만약 여러분의 노티피케이션이 진행중이라면 이것은 특별히 유용하다.
number field
이 값은 노티피케이션에 의해 보여지는 현재의 이벤트 갯수를 가리킨다. 그에 알맞는 숫자가 상태바 아이콘 상단에 오버레이된다. 만약 여러분이 이 필드를 사용하고자 한다면, 여러분은 노티피케이션이 처음 생성될 때 “1”을 가지고 시작해야 한다(만약 여러분이 그 값을 업데이트 동안에 0에서 더 큰 어떤 수로 바꾼다면, 그 숫자는 보여지지 않는다).
iconLevel field
이 값은 노티피케이션 아이콘을 위해 사용되는 LevelListDrawable의 현재 레벨을 가리킨다. 여러분은 LevelListDrawable에 정의된 드로어블에 대응하는 이 값을 변경함으로써 상태바에서 아이콘을 애니메이션 할 수 있다. 더 많은 정보에 대해서는 LevelListDrawable 레퍼런스를 보라.

여러분의 애플리케이션을 커스터마이징할 수 있는 추가적인 기능들에 대한 더 자세한 정보에 대해서는 노티피케이션Notification 클래스 레퍼런스를 보라.

커스텀 확장 뷰 생성하기(Creating a Custom Expanded View)

디폴트로, “노티피케이션Notifications” 윈도우에서 사용되는 확장 뷰는 기본적인 타이틀과 텍스트 메시지를 포함한다. 이것들은 setLatestEventInfo() 메쏘드의 content Title과 contentText 파라미터에 의해 정의된다. 하지만 여러분은 또한 리모트뷰RemoteViews를 사용해서 확장 뷰에 대한 커스텀 레이아웃을 정의할 수 있다. 위에 보이는 스크린샷은 LinearLayout에 이미지 뷰와 텍스트 뷰를 사용한 커스텀 확장 뷰의 예제이다.

확장 메시지에 대한 여러분 자신의 레이아웃을 정의하기 위해서는, 리모트뷰RemoteViews 오브젝트를 인스턴스화하고, 여러분의 노티피케이션의 컨텐트뷰contentView 필드에 리모트뷰RemoteView를 전달하라. contentIntent 필드에는 PendingIntent를 전달하라.

커스텀 확장 뷰를 생성하는 것은 예제를 통해 가장 잘 이해된다.

  1. 확장 뷰에 대한 XML 레이아웃을 생성하라. 예를 들어 custom_notificat ion_layout.xml 이라 불리는 레이아웃 파일을 생성하고, 그것을 다음과 같이 만들어라.
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="horizontal"
                  android:layout_width="fill_parent"
                  android:layout_height="fill_parent"
                  android:padding="3dp"
                  >
        <ImageView android:id="@+id/image"
                  android:layout_width="wrap_content"
                  android:layout_height="fill_parent"
                  android:layout_marginRight="10dp"
                  />
        <TextView android:id="@+id/text"
                  android:layout_width="wrap_content"
                  android:layout_height="fill_parent"
                  android:textColor="#000"
                  />
    </LinearLayout>
    

    이 레이아웃은 확장 뷰를 위해 사용된다. 그러나 이미지 뷰와 텍스트 뷰의 컨텐트는 여전히 애플리케이션에 의해 정의될 필요가 있다. 리모트뷰RemoteViews는 이 컨텐트를 여러분으로 하여금 정의할 수 있도록 하는 약간의 편리한 메쏘드들을 제공한다.

  2. 애플리케이션 코드에서, 이미지와 텍스트를 정의하기 위해서는 리모트뷰RemoteViews 메쏘드를 사용하라. 그런 다음에 아래의 예제에서 보여지듯이 리모트뷰RemoteViews 오브젝트를 노티피케이션의 컨텐트뷰contentView 필드에 전달하라.
    RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
    contentView.setImageViewResource(R.id.image, R.drawable.notification_image);
    contentView.setTextViewText(R.id.text, "Hello, this message is in a custom expanded view");
    notification.contentView = contentView;
    

    여기에서 보여지듯이, 애플리케이션의 패키지 이름과 레이아웃 리소스 ID를 리모트뷰RemoteViews 생성자constructor에게 전달하라. 그런 다음에, setImageViewResource()와 setTextViewText()를 사용해서 이미지 뷰와 텍스트 뷰에 대한 내용을 정의하라. 각각의 경우에서, 해당 뷰에 대한 값과 함께 여러분이 설정하고자 하는 적절한 뷰 오브젝트의 레퍼런스 ID를 전달하라. 최종적으로 그 리모트뷰RemoteViews 오브젝트는 컨텐트뷰contentView 필드로 노티피케이션에게 전달된다.

  3. 여러분이 커스텀 뷰를 사용할 때 setLatestEventInfo() 메쏘드를 필요로 하지 않기 때문에, 여러분은 이 예제에서처럼 contentIntent 필드를 사용해서 노티피케이션에 대한 인텐트를 정의해야 한다.
    Intent notificationIntent = new Intent(this, MyClass.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    notification.contentIntent = contentIntent;
    
  4.  노티피케이션은 이제 보통의 경우처럼 전송될 수 있다.
    mNotificationManager.notify(CUSTOM_VIEW_ID, notification);

리모트뷰RemoteViews 클래스는 또한 여러분의 노티피케이션의 확장 뷰에 측정기Chronometer 또는 진행상태바ProgressBar를 쉽게 추가하기 위해 사용할 수 있는 메쏘드들을 포함한다. 리모트뷰RemoteViews 를 사용해서 커스텀 레이아웃을 생성하는 것에 대한 더 자세한 정보에 대해서는 리모트뷰RemoteViews 클래스 레퍼런스를 참고하라.

Note: 커스텀 확장 뷰를 생성할 때 여러분은 특별히 여러분의 커스텀 레이아웃이 다른 디바이스 오리엔테이션과 해상도에서 적절하게 기능하는 지를 보장하기 위해 주의해야 한다. 이 조언은 안드로이드 상에 생성된 모든 뷰 레이아웃에 적용되지만, 그것은 특별히 이 경우에 중요하다. 왜냐하면 여러분의 레이아웃 실소유물real estate이 매우 제한되기 때문이다. 그러므로 여러분의 커스텀 레이아웃을 너무 복잡하게 만들지 마라. 그리고 다양한 설정에서 그것을 확실히 테스트하라.

'안드로이드' 카테고리의 다른 글

java.lang.Annotation(주석:@)  (0) 2011.01.27
View Fipper  (0) 2011.01.27
PowerManager  (0) 2011.01.27
Accelerometer, orientation  (0) 2011.01.27
Notification Manager  (0) 2011.01.27

Device의 Power state를 제어할 수 있게 해준다. Device 베터리 사용시간은 이 API의 사용에 따라 영향을 받게 된다. 따라서 반드시 필요한 경우가 아니라면 Wakelock을 acquire하지 않는 것이 좋다. 가능한 낮은 레벨을 사용하고 사용후에는 바로 release하는 것이 좋다.

PowerManager pm = (PowerManager)Context.getSystemServier(Context.POWER_SERVICE);

  아주 기초적인 API인 newWakeLock()를 사용할 것이다.
  이 API는 PowerManager.WakeLock객체를 만든다. 
  device의 powerstate를 제어하기 위해 이 객체의 method를 사용하게 된다.

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
wl.acquire();
// ..screen will stay on during this section..
wl.release();


  System 전원에 영향을 미치는 flag가 아래와 같이 정의되어 있다.
PARTIAL_WAKE_LOCK : CPU(On*), Screen(OFF), Keyboard(OFF)
SCREEN_DIM_WAKE_LOCK : CPU(On), Screen(Dim), Keyboard(OFF)
SCREEN_BRIGHT_WAKE_LOCK : CPU(On), Screen(Bright), Keyboard(OFF)
FULL_WAKE_LOCK : CPU(On), Screen(Bright), Keyboard(Bright)

cf) PARTIAL_WAKE_LOCK를 유지하는 동안 CPU는 계속 run상태로 된다. 어떤 타이머나 사용자가 power button을 누르더라도 상태는 유지가 된다. 나머지 wakelock은 CPU가 run상태가 되지만 사용자가 power button을 눌러서 sleep으로 갈 수 있다.

  Screen에만 적용되는 2가지 flag가 더 있다. 이 플래그는 PARKTIAL_WAKE_LOCK에는 영향을 안준다.
1. ACQUIRE_CAUSES_WAKEUP
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately

2. ON_AFTER_RELEASE
if this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer. This can be used to redece flicker if you are cycling between wake lock conditions.


PowerManager는 Screen을 On/Off할때 WindowManager에 통지를 하고 이는 KeyguardViewMediator를 통해 LockScreen에도 전달된다. 
PowerManagerService.mNotificationTask 
  -> WindowManagerPolicy.screenTurnedOn() 
      -> KeyguardViewMediator.onScreenTurnedOn(); 
  -> WindowManagerPolicy.screenTurnedOff() 
      -> KeyguardViewMediator.onScreenTurnedOff();

출처 : http://pheadra.tistory.com/category/Android/Android%20Service

'안드로이드' 카테고리의 다른 글

View Fipper  (0) 2011.01.27
상태바(Status Bar)  (0) 2011.01.27
Accelerometer, orientation  (0) 2011.01.27
Notification Manager  (0) 2011.01.27
Android: gdb를 이용한 Dalvik VM 디버깅  (0) 2011.01.27

1. 센서 관리자

센서 관리자는 안드로이드 장치에서 이용할 수 있는 센서 하드웨어를 관리하는 데 사용

센서 레퍼런스를 얻는 법

String service_name = Context.SENSOR_SERVICE;

SensorManager sensorManager = (SensorManager)getSystemService(service_name);

 

다음 보인 패턴은 이용할 수 있는 모든 하드웨어 센서로부터의 센서 결과를 모니터 하는 데 이용할 수 있다.

SensorListener mySensorListener = new SensorListener() {

           Public void onSensorChanged(int sensor, float[] values){

           // 센서 값 변화를 다룬다.

           }

          

           Public void onAccracyChanged(int sensor, int accuracy){

           //정확도의 변화를 다룬다.

           }

};

 

센서의 정확도 변화를 보려면 onAccracyChanged을 구현해야 한다. Accuracy 매개변수는 다음과 같은 상수 이다.

SensorManager.SENSOR_STATUS_ACCURACY_HIGH 센서가 가능한 가장 높은 정확도

SensorManager.SENSOR_STATUS_ACCURACY_LOW 센서가 가능한 가장 낮은 정확도

SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM 센서가 가능한 가장 높은 정확도

SensorManager.SENSOR_STATUS_UNRELIABLE 센서 데이터를 신뢰할 수 없음을 나타내며, 조정이필요하거나 현재 측정이 불가능함

 

SensorManager.SENSOR_ACCELEROMETER 현재 가속도를 세 축을 따라 제곱 초당 미터(m/s2)로 리턴하는 가속도 센서.

SensorManager.SENSOR_ORIENTATION 세 축에 대한 현재 방향을 도 단위로 리턴하는 방향센서

SensorManager.SENSOR_LIGHT 주변 조도를 럭스로 기술한 단일 값을 리턴하는 주변 광 센서

SensorManager.SENSOR_MAGNETIC_FIELD 현재 자기장을 세 축을 따라 마이크로테슬라로 측정하는 데 사용되는 센서

SensorManager.SENSOR_PROXIMITY 장치와 대상 물체 간의 거리를 미터로 기술한 단일 값을 리턴하는 근접 센서

SensorManager.SENSOR_TEMPERATURE 주변 온도를 섭씨로 리턴하는 온도 센서

 

특정 센서로부터의 변화를 통지받으려면, 센서 리스너를 만들고, 이 리스너로 트리거 해야하는 센서와 센서의 업데이트 속도를 지정하는 센서 관리자에 이를 등록한다.

sensorManager.registerListener(mySensorListener, SensorManager.SENSOR_TRICORDER, SensorManager.SENSOR_DELAY_FESTEST);

업데이트 속도 관련 상수

SensorManager.SENSOR_DELAY_FESTEST 가장 빠른 업데이트 속도

SensorManager.SENSOR_DELAY_GAME 게임 제어에 가장 적합한 업데이트 속도

SensorManager.SENSOR_DELAY_NORMAL 기본 업데이트 속도

SensorManager.SENSOR_DELAY_UI UI 기능 업데이트에 적합한 속도

 

sensorManager.registerListener(mySensorListener, SensorManager.SENSOR_TRICORDER); // default NORMAL

 

2. 가속도 센서와 나침반 이용하기

 - 하드웨어의 현재 방향 측정

 - 방향 변화 모니터

 - 사용자가 향하고 있는 방향 식별

 -   아래,   ,   뒤 방향 가속도(이동 속도의 변화) 모니터

 

Accelerometer는 가속도를 측정하는데 이용된다.

가속도는 속도의 변화 비율로 정의되므로, 가속도 센서는 장치의 속도가 주어진 방향에서 얼마나 빨리 변화하고 있는지를 측정할 수 있고, 사용자의 움직임과 이 움직임의 속도 변화 비율을 감지 할 수 있다.

 

주의) 가속도 센서는 속도를 측정하지 않는다 그래서 속도를 측정하려면, 시간에 대한 가속도의 변화를 측정해야 할 필요가 있다.

 

가속도 센서는 움직임에 기인한 가속도와 중력에 기인한 가속도 간을 구분하지 못한다. 따라서 Z축에 대한 가속도를 감지하는 가속도 센서는 정지 상태에 있을 때 -9.8m/s2 을 읽을 것이다.(이 값은 SensorManager.STANDARD_GRAVITY 상수)

 

가속도 변화 감지하기

가속도는 앞-(세로 Longitudinal), -(가로 Lateral), -아래(수직 Vertical) 이렇게 세 방향 축을 따라 측정될 수 있다.

수직 양의 값은 위, 음의 값은 아래

세로 양의 값은 앞, 음의 값은 뒤

가로 양의 값은 오른쪽, 음의 값은 왼쪽

좌표 축 정의
X축 : 화면에 수평축(portrait 모드에서 짧은 에지를 landscape 모드에서 긴 에지)을 나타내며 오른쪽을 가리킨다.
Y축 : 화면에 수직축을 나타내며 화면 위쪽을 가리킨다.(원점은 왼쪽 밑 코너)
Z축 : 단말이 화면을 위로 해서 테이블에 올려져 있다고 생각했을 때 하늘을 가리킨다.


Orientation (Portrait, Landscape)이 바뀌었을 때 좌표값 역시 바뀐다는 것을 기억해야 합니다. 바뀌지 않은 값을 사용하기 위해서는 배열의 3, 4, 5번째 값을 가져올 수 있습니다.
 


SensorManager sm = (SensorManager)getSystemServie(Context.SENSOR_SERVICE);

Sm.registerListener(mySensorListener,

SensorManager.SENSOR_ACCELEROMETER,

SensorManager.SENSOR_DELAY_UI);

SensorListener mySensorListener = new SensorListener() {

Public void onSensorChanged(int sensor, float[] values){

           // 센서 값 변화를 다룬다.

           If(sensor == SenSorManager.SENSOR_ACCELEROMETER) {

               Float xAxis = values[SensorManager.DATA_X]; //   

               Float yAxis = values[SensorManager.DATA_Y]; //   

               Float zAxis = values[SensorManager.DATA_Z];//

                    

               Float raw_xAxis = values[SensorManager.RAW_DATA_X];

               Float raw_yAxis = values[SensorManager.RAW_DATA_Y];

               Float raw_zAxis = values[SensorManager.RAW_DATA_Z];

           }

}

};

 


Orientation
SENSOR_ORIENTATION (방향 센서)
각 배열의 값은 각도를 나타냅니다.  
단말기 화면이 하늘을 향한 상태로 테이블 위에 수평으로 놓여있는 상태를 기준으로 각 축을 중심으로 회전시키는 것을 생각하면 됩니다.

values[0] : Z 축을 중심으로 회전 (0 <= azimuth < 360) 
                    0  = 북, 90 = 동, 180 = 남, 270 = 서

values[1] : X축을 중심으로 회전 (-180 <= pitch <= 180) 
                   Z축이 Y축 방향으로 향하면 0보다 큰값
                   화면이 하늘을 향하고 테이블위에 수평으로 놓여있는  상태 0, 화면이 아래를 향하면 -180 or 180,  
                   똑바로 세우면 -90, 거꾸로 세우면 +90
                   
values[2] : Y축을 중심으로 회전 (-90 <= roll <= 90) 
                   Z축이 X축 방향으로 향하면 0보다 큰값
 

Accelerometer
SENSOR_ACCELOROMETER (가속도 센서)
각 배열의 값은 (m/s2) 단위로 되어있으며, 접촉힘(Contact Force)을 측정합니다. 

values[0] : X축에 적용되는 힘
values[1] : Y축에 적용되는 힘
values[2] : Z축에 적용되는 힘

예를 들어
  • 단말의 왼쪽 측면을 오른쪽 방향으로 눌렀을때 X 가속도 값은 음수를 나타냅니다.
  • 단말에 테이블위에 평평하게 놓여있을때 -STANDARD_GRAVITY 값, 즉 -9.8 (m/s2) 을 가집니다. 단말이 테이블에서 중력에 대한 반작용으로 적용되는 힘을 나타냅니다. 

    Mgnetic field
    SENSOR_MAGNETIC_FIELD (자기장 센서)
    모든 값은 micro-Tesla (uT) 단위로 되어있으며, X, Y, -Z 축 주변 자기장을 측정합니다.
  • 참고 사이트
    http://blog.androgames.net/85/android-accelerometer-tutorial/
    http://blog.androgames.net/135/android-orientation-tutorial/

    출처 : http://pheadra.tistory.com/category/Android/Android%20Service

    '안드로이드' 카테고리의 다른 글

    상태바(Status Bar)  (0) 2011.01.27
    PowerManager  (0) 2011.01.27
    Notification Manager  (0) 2011.01.27
    Android: gdb를 이용한 Dalvik VM 디버깅  (0) 2011.01.27
    서비스를 부팅시 시작시키기 (Start Service at Boot Time)  (0) 2011.01.27

    1. Notification Manager 얻기
    NotificationManager mNotification = Context.getSystemService(NOTIFICATION_SERVICE);

    2. Notification 객체 만들기
    Notification notifyDetails = new Notification(int icno, CharSequence tickerText, long When);
    ex) Notification notifyDetails = new Notification(res, text, System.currentTimeMillis());

    3. Notification 객체 셋팅하기..
    먼저 Notification 필드를 확인해 봅시다.
    - number :  통지아이콘에 겹쳐서 출력될 숫자를 지정한다. 예를 들어 새로운 메시지가 도착했다는 통지라면 메시지의 개수를 같이 표시할 수 있다. 0이나 음수를 지정하면 숫자가 표시되지 않는다.
    - sound : 통지와 함께 출력할 소리를 Uri객체로 지정한다.
    - vibrate : 진동 방식을 지정한다. 진동할 시간과 멈출 시간을 배열로 전달함으로써 진동의 패턴을 지정한다.
    - ledARGB : 불빛의 색상을 지정한다. 장치에 장착된 LED의 능력에 따라 표현 가능한 색상은 조금 달라질 수 도 있다.
    - ledOnMs, ledOffMS : LED를 켤 시간과 끌 시간을 1/1000초(밀리세컨드) 단위로 지정한다. 이 두 값을 LED의 점멸 주기를 결정한다. 정확하지는 않지만 가급적 근접한 시간을 지킨다.
    - default : 디폴트로 취할 통지 전달 방식을 지정
       DEFAULT_SOUND : 소리 발생
       DEFAULT_VIBRATE : 진동을 발생
       DEFAULT_LIGHTS : 불빛을 깜빡 거린다.
       DEFAULT_ALL : 세가지 동작 다.
    - flag : 통지의 동작 방식을 지정
      FLAG_AUTO_CANCEL : 통지를 누르면 자동으로 통지를 취소
      FLAG_INSISTENT : 취소하거나 상태란을 확장하기 전까지 소리를 계속 발생
      FLAG_NO_CLEAR : 사용자가 clear all 을 선택할 때 취소한다.
      FLAG_ONGOING_EVENT : 계속 진행중인 이벤트를 참조
      FLAG_ONLY_ALERT_ONCE : 이전에 취소된 통지라도 매번 소리와 진동을 발생
      FLAG_SHOW_FIGHTS : LED 불빛을 출력한다.


    4. 확장 상태란과 선택시에 동작
    Void setLatestEventInfo (context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent);
    통지가 발생시킨 주체와 제목, 메시지 내용 문자열 등을 지정하고 통지뷰를 눌렀을 때 호출할 인텐트도 지정.


    5. 마지막으로 통지를 등록하거나 취소하기
    void notify(int id, Notification notification);
    void cancel(int id);
    void cancelAll();
    * notify는 첫번째 인자 id는 통지 객체의 고유한 식별 번호이다. 한 프로그램 내에서 유일한 값이어야 한다.
      이 id를 통해서 통지 객체의 아이콘을 변경하거나 취소할 때 사용할 수 있다.

    6. Tip
    한프로그램내에서 여러개의 통지를 등록한 상태에서 Intent를 실행시 마지막에 등록된 통지만 Intent할 때가 있다.
    이럴때는 PendingIntent의 requestCode를 다르게 주면된다.
    PendingIntent content = PendingIntent.getActivity(SmartControl.this, noti_id, intent, Intent.FLAG_ACTIVITY_NEW_TASK );
    그래도 잘 안될땐.. 플래그를 줘보자 ㅋㅋ Intent.FLAG_ACTIVITY_NEW_TASK

    7. 예제 소스
    1. NotificationManager mNotification = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);  
    2. Notification notifyDetails =   
    3.     new Notification(res, text, System.currentTimeMillis());  
    4.   
    5. long[] vibrate = { 100100200300 };  
    6. notifyDetails.vibrate = vibrate;  
    7. notifyDetails.defaults = Notification.DEFAULT_ALL;  
    8. notifyDetails.flags |= Notification.FLAG_NO_CLEAR;  
    9.   
    10. Intent intent = new Intent(SmartControl.this,  
    11.         DialogMacroLoopEdit.class);  
    12.   
    13. PendingIntent content =   
    14.     PendingIntent.getActivity(SmartControl.this, noti_id, intent, Intent.FLAG_ACTIVITY_NEW_TASK );  
    15.   
    16. notifyDetails.setLatestEventInfo(SmartControl.this,"Title""Test", content);  
    17. mNotificationManager.notify(noti_id, notifyDetails);  

    Dalvik VM 혹은 Android의 시스템 일부를 수정하다 보면 GDB를 이용하여 디버깅이 필요한 경우가 있다. 이 글에서는 Dalvik VM의 내부를 수정한 경우를 가정하여 GDB로 어떻게 디버깅이 가능한지 (간단히) 정리해보도록 하겠다.

    1. Dalvik VM 코드 수정 및 빌드
    Dalvik의 인터프리터 성능을 향상시키기 위해 ARMv5용 코드를 수정한다고 가정하자. 관련 코드는 Dalvik의 vm/mterp/armv5te에 있는 header, footer 소스를 수정하거나 혹은 mterp/out 폴더에 있는 InterpAsm-armv5te.S를 수정하면 될 것이다. (mterp의 전반적인 구조에 대해서는 mterp의 README.txt 파일을 참고하도록 하자.)

    수정한 Dalvik VM을 에뮬레이터 환경으로 빌드하는 방법은 내가 작성한 예전 블로그 글을 참고하시기 바란다. 또한, 빌드한 system image를 Android SDK에 엮어서 사용하기 위한 방법은 이 글을 참고하시길 바란다.


    2. 테스트를 위한 Android 프로그램 실행
    Android SDK와 Eclipse가 설치되어 있다면 내가 수정한 Dalvik VM 코드가 정상적으로 동작하는지 확인하기 위해 간단한 예제 Android 프로그램을 작성하여 에뮬레이터에서 실행해보자. 이 과정을 수행하기 전에 1번의 과정(Dalvik 수정/빌드 및 빌드한 system image를 Android SDK에 복사하는 과정)을 마쳐두어야 한다.


    3. adb shell을 이용하여 GDB server 구동
    에뮬레이터에서 실행되고 있는 Android 프로그램에 GDB를 연결하여 Dalvik VM을 디버깅하기 위해서는 우선 에뮬레이터에서 GDB server를 구동해주어야 한다.
    • android sdk의 tools 폴더에 있는 adb로 adb shell을 실행한다. (adb에 대한 자세한 설명은 Google 문서를 참고하시길)
    • adb shell에서 내가 작성한 예제 프로그램의 pid를 확인하기 위해 ps 명령어를 실행한다.
    • ps의 출력 결과를 보면 자신이 작성한 프로그램의 패키지 이름(예: com.wonkim)을 가진 항목이 있을 것이다. 이 pid를 이용하여 GDB server를 구동한다.
    • adb shell에서 "gdbserver :4000 --attach 324"와 같이 수행한다. 4000은 GDB server의 port이고 324는 예제 프로그램의 pid 값이다. 물론, 이 값들은 자신의 환경에 맞게 변경해서 사용하면 된다.
    • 정상적으로 GDB server가 구동되면 Attached: pid = 324 와 같은 출력을 볼 수 있다.

    4. Host에서 GDB client로 GDB server에 연결
    에뮬레이터의 GDB server에 붙는 GDB client를 실행하기 위해서는 다음과 같은 절차를 따른다.
    • 우선 adb forward를 이용하여 host의 port를 에뮬레이터의 port로 forward 할 수 있도록 설정한다.
      adb forward tcp:4000 tcp:4000
    • GDB client는 Android 빌드 환경에 있는 것을 사용해야 한다. 내 경우는 /workspace/android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin 폴더에 있는 arm-eabi-gdb를 사용하였다.
    • arm-eabi-gdb를 실행할 때 Android 응용프로그램 process에 해당하는 app_process 프로그램을 인자로 지정해주어야 한다. 내 빌드환경에서는 아래와 같이 실행하였다.
      arm-eabi-gdb /workspace/android/out/target/product/generic/system/bin/app_process
    • GDB client를 실행한 후 적절한 symbol을 로딩할 수 있도록 환경을 설정해주어야 한다. 내 빌드환경에서는 아래와 같이 실행하였다. 
      set solib-search-path /workspace/android/out/target/product/generic/symbols/system/lib
    •  이제 에뮬레이터에서 실행되고 있는 GDB server에 리모트로 접속하도록 한다.
      target remote :4000
    • 정상적으로 실행된다면 Remote debugging using : 4000과 같은 메시지가 출력되고 symbol이 정상적으로 로딩될 것이다.
    •  
      5. Break point 설정 및 디버깅
      이제 내가 원하는 소스 코드 위치에 break point를 설정하고 Android 프로그램을 이용하여 Dalvik VM을 디버깅할 수 있다.
      • 내가 수정한 부분이 InterpAsm-armv5te.S 파일의 10143 라인 부터라고 가정하자.
      • GDB client에서 아래와 같이 실행하여 위 라인에 break point를 설정한다.
        break InterpAsm-armv5te.S:10143
      • 정상적으로 break point가 설정되어 있는지는 info breakpoints 명령으로 확인 가능하다.
      • 이제 에뮬레이터에서 Android 프로그램에 적절한 이벤트를 주어 내가 설정한 break point에 도달하도록 만든다. (키를 입력하거나 터치 이벤트를 주거나 등등)
      • Break point에 도달하면 에뮬레이터에서는 프로그램 응답이 없다며 강제 종료를 요청한다. 그냥 무시하고 wait 하도록 만들자.
      • GDB에서 step trace를 하려면 next, step 등등의 명령어를 이용하면 된다. backtrace 명령을 사용하면 stack trace를 볼 수 있다. GDB에 대한 자세한 내용은 이 웹페이지를 참고하시길.

      실제로 이와 같은 과정을 통해 개인적으로 수정해보았던 Dalvik VM의 인터프리터 코드 문제를 쉽게 해결하였다. 이런 tool을 사용하지 않으면 무지 시간이 오래 걸리고 고생 했을 것이다. 

      참고자료:

      출처 : http://androidkr.blogspot.com/2010/10/android-gdb-dalvik-vm.html

      + Recent posts