2012년 4월 17일 화요일

[안드로이드] SD 마운트-탈착에 관한 인텐트



SD 카드에 관련한 작업을 하다보면 예외적인 상황을 고려하지 않을 수 없다. 작업중에 강제로 SD카드가 뽑히거나, USB 연결등으로 마운트 해제가 될수도 있기 때문이다. 그러한 경우 다행히 Broadcast가 날라 오므로 필요에 맞게 받아 처리 할 수 있다.
  • Intent.ACTION_MEDIA_MOUNTED
SD 카드가 마운트 되면 날라옴
  • Intent.ACTION_MEDIA_EJECT
SD 카드가 강제로 뽑히거나 유저가 마운트 해제 하였을때 날라옴
  • Intent.ACTION_MEDIA_UNMOUNTED
유저가 마운트 해제 하였을때 날라옴
  • Intent.ACTION_MEDIA_REMOVED
SD 카드가 장치에서 제거 되었을때 날라옴
  • Intent.ACTION_MEDIA_BAD_REMOVAL
유저가 마운트 해제 하지 않은체 SD 카드가 강제로 뽑혔을때 날라옴

실제로 SD카드를 강제로 뽑아 보면 다음 순으로 발생 함
Intent.ACTION_MEDIA_BAD_REMOVAL -> Intent.ACTION_MEDIA_EJECT

마운트 해제 하면 다음 순으로 발생함
Intent.ACTION_MEDIA_EJECT -> Intent.ACTION_MEDIA_UNMOUNTED

SD 카드를 새로 꼽으면 다음 순으로 발생함
Intent.ACTION_MEDIA_UNMOUNTED -> Intent.ACTION_MEDIA_MOUNTED
희안하게도 언마운트가 먼저 호출되니 처리시 주의가 필요함

참고로 위 인텐트들을 등록 할 때는 아래와 같이 Data scheme을 지정해 줘야 한다.
<receiver android:name=".TestBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_EJECT" />
        <action android:name="android.intent.action.MEDIA_BAD_REMOVAL" />
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <action android:name="android.intent.action.MEDIA_REMOVED" />
        <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
        <data android:scheme="file"></data>
    </intent-filter>
</receiver> 

[안드로이드] 브로드케스트리시버 동적으로 등록/해지 하기

브로드케스트 인텐트를 받기 위해
보통 AndroidManifest.xml 파일에 리시버를 등록 하곤 한다.
이렇게 하면 해당 브로드케스트가 발생하면 항상 실행되게 되는데, 기능에 따라 On/Off 를 하고 싶을 경우가 있다.
뭐 그런 경우 브로드케스트 리시버에서 처리를 안해도 되지만 아예 꺼버린다면 여러모로 효율이 좋게된다.

* 먼저 브로드케스트 리시버를 등록 하는 법
// 인텐트 필터 생성
IntentFilter filter = new IntentFilter();
filter.addAction("com.hermina.broadcast.TEST");
// 브로드케스트 리시버 등록
registerReceiver(reciever, filter);
물론 reciever는 등록할 BroadcastReciever 이며
다룰 인텐트에 따라 필터를 좀더 명확 하게 설정할 수 있다.
filter.addCategory
filter.addDataType
filter.addDataScheme
* 해지 하는법
unregisterReceiver(reciever);

2012년 4월 3일 화요일

[안드로이드] 테마 적용 + 배경이 투명한 액티비티 만들기

안드로이드 ICS가 출시 되면서 Holo.Light 테마가 생겨 났다.
그러한 테마를 적용 하면서 투명 액티비티를 띄우면 투명 액티비티만 테마가 후리다;;;

인터넷에서 찾아 보면 투명 액티비티를 만드는 테마는 다음과 같다.
<activity android:name="Test" android:theme="@android:style/Theme.Translucent">
물론 잘 동작 한다.
하지만 위에서 적었듯이 투명 액티비티에서 보여지는 위젯들은 테마가 적용이 않된 후린 기본 스타일로 보여지게 된다.

내 경우 다이얼로그를 띄워야 하는데 다른 다이얼로그는 테마가 적용 됬는데 투명액티비티에서 띄우는 녀석만 미운오리새끼 같다.

그럼 어떻게 해야 하는가!!

일단 테마를 만듭니다.
project/res/values/themes.xml 을 추가 하면 된다.
다음과 같이 themes.xml파일의 내용을 체운다.
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="Translucent_Dialog" parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>        
    </style>

</resources>
이미 themes.xml 파일이 있다면 style 항목만 추가 하면 되겠죠?

style 에서 "parent" 부분이 상속받을 스타일이고 "item" 으로 배경을 투명하게 지정 한것이다.
사실 "@android:style/Theme.Holo.Light.Dialog.NoActionBar" 테마는 투명액티비티에 필요한 "NoActionBar"라던가 "Dialog"라던가 하는 스타일이 거의 지정 되어 있다.
현재 사용하는 테마 스타일이 다른것이라면 아래와 같은 속성을 추가해 타이틀과 액션바를 없애줘야 할 것 이다.
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
테마가 적용된 투명한 새로운 테마를 생성하였으면 이를 액티비티에 적용해 줘야 한다.
"AndroidManifest.xml" 파일을 열어 투명효과를 적용할 액티비티에 다음과 같이 테마를 지정해 준다.
<activity
    android:name=".TrasparentTestActivity"
    android:label="@string/app_name"
    android:theme="@style/Translucent_Dialog" >
이렇게 하면 투명한 액티비티 생성 완료~

2012년 3월 22일 목요일

[안드로이드] 사용자 정의 permission의 선언

------------ 요기는 삽질 내용입니다. permission의 선언법만 궁금하시면 넘어가세요.
간단한  ContentProvider를 테스트 하기 위해 샘플 프로젝트를 만들어 보았다. 액티비티 하나,  ContentProvider  하나. 이 App를 CPTestApp1 이라 명명해 보았다.

<provider
            android:name="TestContentProvider"
            android:authorities="com.hermina.testcontent">
</provider>


잘동작한다.

이번엔 신규 App CPTestApp2를 생성해 CPTestApp1에서 만든  ContentProvider를 사용해 보기로 하였다.
뭐 잘 된다.

이번엔 CPTestApp1의 ContentProvider에 permission을 지정 해 보았다. 기왕 하는거 퍼미션명도 다르게 지정해 보았다.

<provider
            android:name="TestContentProvider"
            android:authorities="com.hermina.testcontent"
            android:readPermission="com.hermina.permission.CP_READ" >
</provider>



그리고 나서 CPTestApp2에 해당 권한을 넣어 준다.
<uses-permission android:name="com.hermina.permission.CP_READ"/>

젠장 이번엔 안된다.
"com.hermina.testcontent"에 접근 하려면 "com.hermina.permission.CP_READ"권한이 있어야 한다는데...

이쯤에서 인터넷 검색해보고 별짓 다해봤다. ContentProvider에 관련된 자료와 책을 보고 또보고 다른 소스 코드들을 열심히 보았지만 이유를 몰랐다.
그러던중 <permission android:name="xxx.xxxxxx.xxxxx" /> 를 발견하게되고...
분을 삭힐수 있게 되었는데...
어릴땐 책을 사면 아까워서 글자 한톨 그림 구석구석 정독을 했었다. 하지만 언제부턴가 책을 사도 빠르게 필요한 부분만 뽑아 보게 되었다. 내가 만약 안드로이드 permission에 관해 먼저 자세히 알고 있었다면 이런 한심한 삽질은 하지 않았으랴...

------------ 본론입니다.
안드로이드에서 사용자 정의 퍼미션을 선언하는 방법은 다음과 같다.

<receiver android:name=".NewPhotoReceiver">

컨텐트 프로바이더에서 해당 퍼미션을 사용 하려면 다음과 같이 하면 되고

<provider
            android:name="TestContentProvider"
            android:authorities="com.hermina.testcontent"
            android:readPermission=" xxx.xxxxxx.xxxxx " >
</provider>


이를 사용 하는 어플리케이션에서 해당퍼미션을 허용하려면

<uses-permission android:name=" xxx.xxxxxx.xxxxx "/>

처럼 사용 하면 된다.

2012년 3월 20일 화요일

[안드로이드] Broadcast - 카메라로 사진을 찍을 경우 발생하는 인텐트



카메라에서 사진을 찍으면 com.android.camera.NEW_PICTURE 라는 브로드 케스트가 생성 된다.
이걸 처리하는 인텐트 필터를 선언 하고 브로드 케스트 리시버를 만들어 주면 사진이 찍힐때 마다 하고자 하는 일을 할 수 있게 된다.

* XML 선언 예
<receiver android:name=".NewPhotoReceiver">
<intent-filter>
<action android:name="com.android.camera.NEW_PICTURE" />
<data android:mimeType="image/*"/>
</intent-filter>
</receiver>

* 브로드케스트리시버 샘플
public class NewPhotoReceiver extends BroadcastReceiver
{
 @Override
 public void onReceive(Context context, Intent intent) {
  Log.d("Test", "START OF NewPhotoReceiver");
  
  Uri uri = intent.getData();
  
  Toast.makeText(context, "Photo taken - " + uri, Toast.LENGTH_SHORT).show();
  
  Log.d("Test", "[onReceive] URI - " + uri);
 }
}

[안드로이드] animation 리소스의 특정 프레임을 Bitmap으로 바꾸는 법

android.R.drawable.stat_sys_upload
이녀석은 움직이는 이미지다.
따라서 다음과 같이 하면 null값이 리턴되는데...
Resources res = ctx.getResources();
Bitmap bmpOneFrame = BitmapFactory.decodeResource(res, 
                                                android.R.drawable.stat_sys_upload);
그럼 움직이는 이미지의 특정 프레임을 Bitmap으로 얻어 오려면 어떻게 해야 할까?

1. 먼저 움직이는 이미지의 리소스 ID로 AnimationDrawable을 얻어 온다.
2. AnimationDrawabled의 특정 프레임을 Drawable로 얻어온다.
3. Drawable을 Bitmap으로 변환 한다.

이것을 코드로 나타내면,

public static Bitmap getFrameToBitmap(Context ctx, int resId, int nFrame)
{
    Resources res = ctx.getResources();     
    Bitmap bmpOneFrame = BitmapFactory.decodeResource(res, resId);
     
    if(bmpOneFrame == null)
    {
        AnimationDrawable ani = (AnimationDrawable)res.getDrawable(resId);
        Drawable drawableFrame = ani.getFrame(nFrame);
   
        bmpOneFrame = Bitmap.createBitmap(drawableFrame.getIntrinsicWidth(), 
                                          drawableFrame.getIntrinsicHeight(), 
                                          Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmpOneFrame);
        drawableFrame.setBounds(0, 0, drawableFrame.getIntrinsicWidth(), 
                                      drawableFrame.getIntrinsicHeight());
        drawableFrame.draw(canvas);
    }
  
    return bmpFirstFrame;
}
참고로 리소스가 그냥 Drawable인 경우는 바로 Bitmap이 리턴된다.
또한 프레임 번호에 관련한 에러 처리는 없다 ^^;;

[안드로이드] ICS에서 Notification에 ProgressBar가 깨짐

ICS에서 RemoteView를 이용해 ProgressBar를 그리면 ProgressBar의 끝부분이 꺽여서 보기 흉해지는 경우가 있다.
원인은 잘모르겠으나 아마 ICS의 새로운  ProgressBar 는 (직접 그리는게 아니고)이미지로 되어 있기 때문인듯 한데...
그렇게 깨지는 경우 padding값이나 margin 값을 주어 ProgressBar의 위치를 조정해 주면된다.
상당히 꼴사나운 방법이지만 다른 방법은 찾지 못했다.
참고로 padding값이나 margin 값을 px가 아닌 dp로 줘야 한다.
요즘엔 폰마다 해상도가 천차 만별 -ㅁ-;;

아 그리고 ProgressBar의 스타일은 예전 버전처럼 주면 꺽이는 문제는 발생하지 않는다.
다만 촌스럽게 보일뿐 ^^;;
뭐 개인차겠지만;;
스타일에 Widget.ProgressBar.Horizontal 지정하여 사용 하시길...