오늘은 DeskClock App에서 Alram  Event를 발생시키는 부분과 Alram BroadCast 처리하는 부분을 살펴 보려고 한다.

최초에 생각은 Process를 띄워서 Alaram BroadCast를 Generating 시킬것이라고 생각했었는데,
소스를 보고나선 역시나... 그따위로 할리가 없었다ㅋㅋㅋ

Android Package 중에 AlaramManager(android.app.AlarmManager)라는 녀석이 있어서, 그 서비스를 이용하는것이다. 

DeskClock 소스내에
com.android.deskclock.Alarams 가 실제로 AlaramManager를 이용하여, Alaram서비스를 등록한다.

    public static long addAlarm(Context context, Alarm alarm) {

        ContentValues values = createContentValues(alarm);

        Uri uri = context.getContentResolver().insert(

                Alarm.Columns.CONTENT_URI, values);

        alarm.id = (int) ContentUris.parseId(uri);


        long timeInMillis = calculateAlarm(alarm);

        if (alarm.enabled) {

            clearSnoozeIfNeeded(context, timeInMillis);

        }

        setNextAlert(context);

        return timeInMillis;

    


addAlaram() Method가 실제로 알람을 추가 하는데,
여기선 직접적으로 AlramManager도 사용하지 않고, Data Handling도 하지 않는다.
단순히, ContentResolver를 통해서 Data를 삽입하고, setNextAlert()을 통해서 다음 알람을 세팅한다.

setNextAlram()는 alram이 있냐 없냐를 봐서 없으면 Disable , 있으면 Enable시키는데, 
일단 관심은 AlaramManager를 사용하는 파트 임으로, 제끼고 
enableAlert() Method를 보도록 하자.

 private static void enableAlert(Context context, final Alarm alarm,

            final long atTimeInMillis) {

        AlarmManager am = (AlarmManager)

                context.getSystemService(Context.ALARM_SERVICE);


        if (Log.LOGV) {

            Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis);

        }


        Intent intent = new Intent(ALARM_ALERT_ACTION);


        Parcel out = Parcel.obtain();

        alarm.writeToParcel(out, 0);

        out.setDataPosition(0);

        intent.putExtra(ALARM_RAW_DATA, out.marshall());


        PendingIntent sender = PendingIntent.getBroadcast(

                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);


        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);


        setStatusBarIcon(context, true);


        Calendar c = Calendar.getInstance();

        c.setTimeInMillis(atTimeInMillis);

        String timeString = formatDayAndTime(context, c);

        saveNextAlarm(context, timeString);

    }


보면 사실 별것은 없고,  AlramManager Object를 얻어오고, Intent를 만들고,
Alram에서 넘어온 시간 값 함꼐 am.set으로 넘겨 주는 이정도 이다.

실제로 사용하는건 참 간단하다고 볼 수 있다.

다음번에는 AlramManager가 trig 하는 BroadCast를 어떻게 받아서 처리 하는지 보면
기본적으로 Alaram Service 사용을 알 수 있다.

내 생각에 추가로 ContentResoulver() 쪽만 스터디 하고 나면, 
Alram App 하나정도는 금방 만들수 있지 않을까 생각한다.



XCode 3.2.5에서는 Base SDK 설정이 simulator로 설정이 안돼고, 
Device로만 선택할 수 있따.

왜 없애 버렸는지 알순 없지만... 기본적으로 "Other.."를 통해서 직접 입력할 수가 있다.

Base SDK 를 설정하면 Compiler 옵션으로 SDKROOT=iphoneosx.x 나 SDKROOT=iphonesimulatorx.x이런식으로 들어 간다.
Other를 선택해서 iphoneosx.x 이런식으로 값을 따로 설정 하면 된다.

위와 같이 iphonesimulator라고 써주면, Base SDK를 Simulator로 설정할 수 있다.

iphonesimulator라고만 쓰면 최신의 sdk 이고,
특정버전이 필요 한 경우에는 iphonesimulator4.2 이런식으로 기술 해주면 된다.

iOS 에서 외부 라이브러리를 사용할때,
가끔 가끔 unrecognized selector에러가 날때가 있다.

이건 참 짜증나는 경운데, 보통은 Objective-C Runtime버그로 이러는 경우가 있다.

라이브러리를 구현한쪽에서 Category를 이용해서 Class를 확장해서 사용하는 경우, 
Category에 정의된 selector를 Objective-C Runtime에서 못찾는 게 되는 경우다. 

Objective-C Runtime에서 라이브러리 Loading을 안한 경우인데,
이때는 Compiler , Other Linker Flag에서 -all_load 혹은 -force_load를 해줌으로써  해결할 수 있다.

Compiler/Runtime은 보통 믿고 사용하는 녀석들인데,
98% 만 믿어야지, 100% 믿으면 안된다 ;ㅂ;


iOS Project에서 Bundle Resource의 Patfh를 가져 오는 방법은 보통 아래와 같아.

NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];


그런데, OCUnit의 Logic Test에서는 위와 같은 방식으로는 접근이 안된다.

아직 정확한 매커니즘은 파악하지 못했지만. 아래와 같은 방식으로 사용할 수 있다고 한다.

NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:name ofType:@"png"];


혹시 왜 그런지 아시는 분 답변좀.. 
나중에 찾아봐야지 하고 있지만, 귀차니즘으로 ㅎ 
Android Manifest.xml은 Android 앱의 "시작과 끝"이다.
Android Manifest를 잘 들여 다 보면, 이 앱이 어떤 하드웨어를 사용하고, 어떤 기능등을 제공하는지 대략적으로 알 수 있다.

AndroidManifest.xml을 볼때 가장 먼저 보는것이 android-permission 부분이다.

DeskClock에서 사용하고 있는 Permission List는 아래와 같다 

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

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

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

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

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

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

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


해당 설정들은 http://developer.android.com/reference/android/Manifest.permission.html 에서 상세 정보를 볼수 있따.

특히한것만 한번 살펴 보면,

WRITE_SETTING - Android Device의 System Setting을 Read/Write할 수 있는 권한  
DISABLE_KEYGUARD - KEYGUARD를 Disable시키는 건데, KeyGuard란 화면 Lock됐을때의 화면이다.
READ_PHONE_STATE - Phone 번호 정보등 폰에 관련된 정보를 얻기 위한 권한 

Activity 들은 UI 단의 Activity 이기 때문에, 제처두고, 

등록되어 있는 Service, Provider, Receiver쪽을 잠깐 살펴보자..

<provider android:name="AlarmProvider" android:authorities="com.android.deskclock" />


이부분은 해당 App.을 "Content Provider"로 사용하겠다는 선언이다.
android:authorities는 Content Provider의 identification 이라고 생각하면 된다.
자세한 정보는 에서 http://developer.android.com/guide/topics/manifest/provider-element.html 확인할 수 있다.


 <receiver android:name="AlarmReceiver">

            <intent-filter>

               <action android:name="com.android.deskclock.ALARM_ALERT" />

               <action android:name="alarm_killed" />

               <action android:name="cancel_snooze" />

            </intent-filter>

 </receiver>

 <service android:name="AlarmKlaxon">

            <intent-filter>

                <action android:name="com.android.deskclock.ALARM_ALERT" />

            </intent-filter>

 </service>


위와 같이 service/receiver 선언 부분이 몇가지 있는데, 
service 는 Android에서 얘기하는 "BroadCast" 를 발생시키는 녀석이고,
receiver는 "BroadCast"를 받아서 처리 하는 녀석이다.

위의 코드를 보면, DeskClock에서는 Alram을 발생시키고, 그리고 자기가 직접 처리 할수 있게 설정되어 있다.
그외에도 몇가지 Receiver들이 선언 되어 있는데,
BOOTING/TIME_SET/TIMEZONE_CHANGED/등등의 BROADCAST를 처리 할 수 있게 
Receiver들이 선언 되어 있다. 

단순한 것이지만, service를 등록하여 alram을 broadcast하고, receiver를 등록하여 alram을 사용자에게 알려주는 기능을 한다는 것을 
AndroidManifest.XML을 보면서 파악할 수 있었다.

이 Manifest 설정을 보면서 소스 코드를 파악해보도록 하자.


DeskClock ? Google사에서 오픈 소스로 여는 시계 어플리케이션이다.


Android 공부도 할겸해서, 분석해보기로 한 어플리케이션! 

Project URL  : http://android.git.kernel.org/?p=platform/packages/apps/DeskClock.git 

이 소스 분석을 통해 기대 하고 있는 부분은, Alaram Service를 위한 Service Provider 구현.
Alaram을 울리기 위한, 진동/사운드 등등 이다.

Project Page 에서 볼 수 있듯이, 소스 코드는 git 를 통해서 다운로드 받는다.

> git clone git://android.git.kernel.org/platform/packages/apps/DeskClock.git



위와 같이 다운로드 받아 진다.

Google에서 공개 하는 Source Code 는 Android Sample역시, Eclipse 에서 import 하도록 되어 있지 않고,
Eclipse의 "Create Project from exising Source"를 통해서 Eclipse에서 Project를 열도록 되어 있다.


적당한 이름을 써주고, Buid Target을 각자의 환경에 맞게 설정하고, 

Run as "Android Applicaton" 으로 실행하면,

프로젝트 실행이 완료 된것이다.


Bot으로 메일 보내는 기능이 필요 해서, SMTP 서버를 돌릴 필요가 생겼습니다.

http://www.phase2technology.com/node/667/


위의 원문 블로그가 있으니, 원문 보실분은 참조 하시길 바랍니다 ㅎ

의레 그러하듯이 OSX의 LaunchDaemon에 SMTP 를 등록해야 합니다.

사실 SMTP는 이미 등록은 되어 있는데, Service로 등록되어 있아, 수정만 해주면 됩니다.

/System/Library/LaunchDaemons/org.postfix.master.plist


를 열어서, </dict> 이 닫히기 전에 

<key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/>

아래 코드를 추가합니다.

Terminal 을 통해서 launchctl cli program을 돌려서 수동으로 서비스를 실행시키거나,
재부팅을 하면 smtp 가 돌아갑니다.

$ sudo launchctl 

launchd% start org.postfix.master


SMTP server자체는 OSX에 이미 들어 가 있기 떄문에,
별로 어려운일은 아닙니다 ㅎㅎ

Tiger나 Leopard에서는 다를 수도 있지만, Snow Leopard에서 sshd를 Enable시키는건 매우 간단합니다.

System Preference -> Sharing -> Remote Login 을 활성화 시켜주면 땡!!!


GUI를 통해 쉽게 할수 있으니 편하다고나 할까,

1. http://sourceforge.net/projects/traconwindows/ 에서 최신 버전 다운로드 받기


2. TOW_HOME설정하기

기본적인으로 설치 경로가 C:\TOW로 잡혀서, 다른 폴더에 설치 할경우 에러가 난다.
set-tow.bat을 열어서 경로를 변경해주면 된다.

3. httpd.conf 에러

이러면서 에러 발생 ㅜ_ㅜ line 42에 에러님이 계시다네요
그래서 스윽 훓어 보고, 스윽 수정하고 싶었는데,

httpd.conf안에 c:\TOW이런식으로 다 패스가 잡혀 있다 ㅜㅜ

다 수정해주니..

잘 동작한다.

이제 잘 사용하는 일만 남았네요 ㅎㅎ
TouchXML을 프로젝트에 붙혀서 사용하려고 하는데, 
내 소스도 아닌 것이 내 프로젝트에 있는것이 영 맘에 들지 않아서, Static Library 로 빌드 해서 사용하려고 시도 중입니다.

iOS 용 Static Library를 만들면, Device 용 iOS Simulator용 두가지 버전을 만들어서 
lipo Tool을 이용해서 universal library로 만들어야하는데, 이게 좀 에러다.. 

Target's Build Configuration의 Architecture를 아무리 Setting해도 Xcode 3.2.5 에서는 이게 무시된다.
Xcode 3.2.4 에서는 Base SDK 가 iOS Device와 iOS Simulator이렇게 나뉘어져 있었는데,
Xcode 3.2.5 에서는 Xcode Setting으로만 존재 하고, build configuration에서는 사라졌다.

결국 Xcode 내에서는 iOS Device용과 iOS Simulator용을 한번에 빌드 하는것이 불가능하다는 얘기다.

Script만 돌리는 Target을 만들어서, 
Xcode가 아닌 xcodebuild 를 이용해서 build 하고 merge 하게 해야 할것 같은데,,, 이건 영 맘에 들지 않는 솔루션이다.

Build Dependancy가, Xcode IDE상에 명확히 나타나지 않기 때문이다.

여튼 현재 내가 아는선에서의 방법은

Build Phase Target을 프로젝트에 추가하고 

1. xcodebuild로 device용 library 빌드
2. xcodebuild로 simulator 용 library 빌드
3. lipo로 각각의 library 를 universal 로 merge

하는 식이다.

혹시 누구 좋은 방법 없나요.. 이건 전혀 Cool 하지 못해요 ;ㅂ; 

+ Recent posts