<EditText android:layout_height="wrap_content" 

android:id="@+id/TimeEditText" android:layout_weight="1" android:layout_width="fill_parent"

android:inputType="number"

>

</EditText>


android:inputType으로 입력받을 수 있는 값을 강제 할수 있다. 생각보다 쉽죠? ㅎ
그러나,  맘에 안드는게 있는데, AVD에서 보면

우외 같이 키보드가 다 나온다는 얘기다. 
터치를 누를수는 있는데, 값은 입력이 안된다. 

그럼 이렇게 숫자만 있는 것을 어떻게 사용할까? 이건 뭐 Java 기초 ㅎ

Integer.parseInt(String .. ) 를 이용한다. 

EditText editText = (EditText) mAlertLayout.findViewById(R.id.TimeEditText);

mins = Integer.parseInt("" + editText.getText());


editText의 값이 null 일것을 대비해서 "" + 를 붙혀서 넣는걸 잊지 말자. 


Twitter API를 구현하는건 매우 귀찮은 일이라 ㅎ
일전에 사용해본 경험이 있는 MGTwitterEngine을 사용해보고자 한다. 

내가 처음 MGTwitterEngine을 사용해본게, 아마도 1년전 즈음이었으니, 소스 구조도 빌드 방법도 많이 바뀌었구나,
내 소감부터 말하면 완전 그지 같다. 

외부 라이브러리를 가져다 쓰니까 외부 라이브러리를 빼놓은것은 이해하지만 그럼 BuildScript를 통해서
Dependency를 좀 해결할수 있게 해주던가 :(

Reference가 깨져서 빨간불 잔뜩이다. 

https://github.com/mattgemmell/MGTwitterEngine

위의 링크를 통해 MGTWitter Engine을 컴파일 해보면, 아래와 같이 에러가 잔뜩이다. 

일단 좌측에 소스가 없는것에 TouchJSON/OAuthConsumer/yaji 가 있는데, OAuthConsumer를 제외하고는 Optional Parser이기 때문에, 아예 Reference를 지워 버리자. 

OAuthConsumer는 OAuth에 필요 하기 때문에 따로 다운로드 받아서 Reference Error를 해결해주도록 하자.
다운로드 링크는 아래와 같다. 

https://github.com/ctshryock/oauthconsumer

다운로드 가 끝나고 나면 OAuthConsumer Group의 Info를 열어서 "Choose" 버튼을 눌러서,
다운로드 경로로 바꿔주자.

그런데, 여전히 빨간불 들어 오는 녀석이 있는데, 이건 MGTwitterEngine쓴애가 오타를 써서 그렇다.
Crypto 가 맞는데 Crytpo로 되어 있다. 
Crytpo Group의 Info를 바꿔서 다운받은 OAuthConsumer 소스 내의 Crypto 로 Path를 변경해주자.

그리고 여전히 한군데 빨간불이 있는데, 


그냥 일단 지워버리자 ㅋㅋ

자 그런 소스구조내의 빨간불은 다 해결했으니, 빌드 ! 그러나 현실은 에러 ㅋㅋㅋ

위의 에러는 우리가 사용하지 않을 yajl 과 TouchJSON 쪽에너 내는데, 
MATwitterEngine내의 "Twitter TouchJSON Parser" 와 "Twitter YAJLParsers" Group을 삭제 하자.


그러고 빌드 하면, 빌드 성공 !


우여 곡절끝에 빌드를 완료 했다.

빌드가 완료되었으니, 소스를 긁어다가 사용하든, 라이브러리화 해서 사용하든 하면 된다.

나는 내가 만든 소스가 아닌 경우 보통 라이브러리화 시켜서 사용하는 편인데, 
일단 귀찮고, 당장에 쓸일도 없어서,  소스 긁어다가 샘플 소스 만든 것 첨부하도록 하겠습니다. 


MGTwitterEngine개발하신분은 정말 고맙지만, 좀 이해하기 힘든 방식으로 배포 하는것 같습니다. 
이렇게 쓰기 귀찮아서야, 버전 업그레이드 된다고 해도 Major 이슈가 없다면, 업그레이드 하지 않을것 같네요.

사실 Major이슈라면, 그냥 제가 고쳐서 -_-+
다들 즐거이 개발하세요 :)

OCUnit 에서 UIImage 의 imageNamed:를 호출하면 
메인 어플과 다르게 UIImage Object를 가져 오지 못하고, nil 을 리턴하면서 프로그램이 좌자작 꼬인다. 

왜 이런 문제가 나타나는 걸까 ?

그건 바로 mainBundle이 꼬여 있기 때문이다. 
OCUnit FrameWork 단에서 mainBundle을 OCUnit Product로 변경해줬어야 할것 같은데, 그게 되어 있지 않다. 

그래서 OCUnit에서 Bundle Resource를 사용할때는 [NSBundle mainBundle]을 사용하지 않고, bundleForClass Method를 사용해서 접근하는데, UIImage imageNamed:는 내부적으로 mainBundle 을 호출하기때문에 이를 피해갈 방법이 없다. 

그렇다면 UIImage imageName:를 쓰지 말아야 하는것일까? 
그렇게 해야만 했다면, Blog에 Posting하지도 않았겠죠? ㅎㅎ

아이디어는 Objective-C의 Category 라는 특성을 이용해서 Method만 Override하는것이다.
( 사실 Method Implementation Switch 로 할려고 했는데,, 잘 안되더라고요 :( ) 

그럼 UIImage 의 imageName:을 Override할것인가 NSBundle의 mainBundle을 Override할것인가를 결정해야 하는데,

TestCode내에서 mainBundle말고, bundleForClass를 부르는것도 짜증났기에, 
NSBundle의 mainBundle 을 재정의 하기로 했다.

소스코드는 매우 간단하다.

@implementation NSBundle(OCUnit)


+(NSBundle *) mainBundle {

return [NSBundle bundleForClass:[TestClass class]];

}

@end


여기서 좀 마음에 들지 않는 부분이 있는데, 
Test하고 있는 Class를 참조 해야 한다는것이다.
<주의 ! TestClass의 Class명은 OCUnit내의 Class로 이름을 변경해주어야함 >
 
결국은 bundleForClass를 통해서 하고 있는데, 좀 다르게 깔끔하게 하는 방법을 좀 찾았으면 좋겠다.
아마도 있겠지만, 일단은 흘러가니, 나중에 라이브러리화를 고려할때, 좀더 생각해봐야 겠다.







NSObjeect 의 Header 파일을 살펴보면 + (void) load; 가 있다. 

이 녀석이 뭐하는 녀석인고 하니..

Class 가 Loading 될때, 자동으로 실행 되는 메소드다.
Java 에서의 {} 와 동일한 녀석인데, 

이걸 어떨때 많이 사용하냐면, 보통 Single-Tone Pattern을 적용할때,
sharedInstance 혹은 shard샬라 샬라 이런식으로 Interface 를 짜고, 
최초에 불러질때, Object를 Create하는 Lazy-Loading 기법을 많이 사용하는데,
종종 어플리케이션이 실행 될때 Loading되는 Static-Loading이 필요 할 때가 있다.

이때 해당 Class 에서 + (void) load를 Override하여 Static-Loading을 할 수 있다.



Android Activity 에서 Dialog띄우는 방법은 꽤 간단하며,
몇 안되는 맘에 드는 구조중 하나다 ㅋㅋ

일단, Dialog를 띄우고 싶은 시점에서 android.app.Activity에 정의 되어 있는 showDialog(int id) Method를 호출해준다.

그러면 자동으로 android.app.Activity의 onCreateDialog이 호출되는데, 
우리는 이 onCreateDialog Method를 재정의 함으로써 Dialog를 띄울수 있다.
onCreateDialog의 return type이 Dialog이고 argument로 id를 맞는다. 

즉 showDialog를 호출 해줄 때, 상황에 맞게 id 값을 넘겨주고, 그에 따라 Dialog를 적절히 만들어서,
return만 해주면 Dialog가 띄워진다는 얘기다.

@Override

protected Dialog onCreateDialog(int id) {

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setMessage("AlertDialog")

      .setCancelable(false)

      .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

          public void onClick(DialogInterface dialog, int id) {

          dialog.cancel();     

          }

      })

      .setNegativeButton("No", new DialogInterface.OnClickListener() {

          public void onClick(DialogInterface dialog, int id) {

                dialog.cancel();

          }

      });

AlertDialog alert = builder.create();

return alert;

}


위의 예제는 하나의 type의 Dialog를 띄워주는 기능을 하기때문에 argument id를 사용하지 않고, 
무조건 Dialog를 만들어서 return 해준다. 
위의 예제코드로 실행을 하면 아래와 같이 Dialog가 나타난다.

Dialog도 이런저런 Type혹은 Customizing이 가능한데, 
필요한게 있으면, 아래 android developer사이트를 참조하여 사용하면 된다. 

참고 자료 : http://developer.android.com/guide/topics/ui/dialogs.html
string.xml 에서 '(apostrophe)를 사용하면 에러가 발생한다.

언제나 그렇듯, 에러 문구를 잘 보면 답이 있는데, 
답은 바로 apostrophe를 사용하기 전에 '\' 를 써줘야 한다는것이다.

위의 스크린 샷 처럼 '\'를 앞에 추가 해주면 에러가 말끔히 사라진다.
아마도 몇몇 특수 문자들은 '\'를 추가 해주어야 하는것 같다.
우선, 시계를 출력하려면 당연히 Timer가 필요하다.

java.util.Timer Class 를 이용해서 일정한 주기로 Task를 실행할 수 있다.

Task는 Method를 넘겨주는게 아니라, 역시 TimerTask의 SubClassing 해서 구현한 다음 해당 Class의 Object를 넘겨줘야한다.

    @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mCurTimeTextView = (TextView) findViewById(R.id.CurrentTimeTextView);

MainTimerTask timerTask = new MainTimerTask();

mTimer = new Timer();

mTimer.schedule(timerTask, 500, 1000);

}


Activity 가 생성될때, 불러지는 onCreate를 Override하여, TimerTask Object와 Timer Object를 만들고, timer의 schedule method를 통하여,
일정 주기로 Task가 호출 될 수 있게 한다.

2번째 Argument가 최초 실행 timing이고, 3번째 Argument가 실행 주기이다. 초단위로 시계를 보여줄것이기 때문에,
1초마다 호출되도록 프로그래밍 하였다.

private Handler mHandler = new Handler();

private Runnable mUpdateTimeTask = new Runnable() {

public void run() {

......

}

};

class MainTimerTask extends TimerTask {

public void run() {

mHandler.post(mUpdateTimeTask);

}

}


MainTimerTask Class구현부가, InnerClass 로 TimerTask를 SubClassing한 형태인데, 
Android에서는 MainThread이외에서는 UI Object를 제어 할 수 없기 때문에,
Handler를 통해서 MainThread에 Task를 넘겨 준다.

즉 시간을 Update하는 부분은 Runnable Object의 Run() Method이다.
Run() Method는 아래와 같이 구현한다.

Date rightNow = new Date();

SimpleDateFormat formatter = new SimpleDateFormat(

"hh:mm:ss dd.MM.yyyy");

String dateString = formatter.format(rightNow);

mCurTimeTextView.setText(dateString);


현재 시간을 가져오기 위해 java.util.Date Class를 사용했으며,
Date를 String으로 변환하기위해 java.text.SimpleDateFormat Class를 사용하였다.
SimpleDateFormat Class에 대한 자세한 사용법은 http://developer.android.com/reference/java/text/SimpleDateFormat.html 를 참고 하길 바란다.
formatter를 통해서 Date String을 만들고 해당 String을 TextView에 setTest로 setting함으로써 시간을 세팅할 수 있다.

이외에 유의 해야 할점은 Activity Life Cycle에 따른 Timer stop/start 이다.

onDestory/onPause 에서 Timer 를 정지 시키고, onResume에서 재 시작해주도록 아래와 같이 프로그래밍 한다.

@Override

protected void onDestroy() {

mTimer.cancel();

super.onDestroy();

}


@Override

protected void onPause() {

mTimer.cancel();

super.onPause();

}


@Override

protected void onResume() {

MainTimerTask timerTask = new MainTimerTask();

mTimer.schedule(timerTask, 500, 3000);

super.onResume();

}


간단한 프로그래밍이지만, Android Life Cycle/ MultiThread에서의 UI 제어등 알아두어야 할것이 꽤 있다.






맨날 쓰면서 자꾸만 까먹는, Path -_ㅜ

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];


이렇게 하면 SandBox내의 Documents Directory의 Path를 가져올 수 있다.


iOS SDK 에서 Email 보내기.

그 옛날, 한국에 아이폰도 없던 그 시절 ( iOS 2버전대.. ) iOS SDK 에서 Email을 보내려면 
External URL Protocol을 이용해서 Email프로그램으로 스위치 했어야 했다.
Suspending/Resume구조 도 없던 시절이라, 그야 말로 내 App은 종료되어 버렸다 ;ㅂ;

NSString *url = [NSString stringWithString: @"mailto:foo@example.com?cc=bar@example.com&subject=Greetings%20from%20Cupertino!&body=Wish%20you%20were%20here!"];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]];
이런식으로 프로그래밍 하였었다.

iOS 3.0 이 나오면 MFMailComposerViewController라는 녀석이 생겼는데,
UIImagePickerViewController처럼, Tunkey 방식으로 사용된다.

적절한 값만 세팅하면 내가 제어 할수 없는 ViewController가 Display되고
거기서 이메일 보내고 지지고 볶고 다 한다.

MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller
.mailComposeDelegate = self;
[controller setSubject:@"My Subject"];
[controller setMessageBody:@"Hello there." isHTML:NO];
[self presentModalViewController:controller animated:YES];
[controller release];
대강 이런식으로 하면 사용할 수 있다..

MFMailComposeViewController는 MessageUI Framework에 포함되어 있음으로,
MessageUI Framework을 추가 시키는걸 잊지 말자.
그리고 MFMailComposeViewControllerDelegate도 필요 하면 구현하면 된다!!




Inspector Window에서 Status Bar를 Unspecified로 두면 된다.

+ Recent posts