#if 1 // DEBUG

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

NSArray *languages = [defaults objectForKey:@"AppleLanguages"];

NSString *currentLanguage = [languages objectAtIndex:0];

NSLog(@"Current Locale: %@", [[NSLocale currentLocale] localeIdentifier]);

NSLog(@"Current language: %@", currentLanguage);

NSLog(@"Welcome Text: %@", NSLocalizedString(@"WelcomeKey", @""));

#endif


2010-12-23 06:48:34.061 Sample[327:207] Current Locale: en_US

2010-12-23 06:48:34.062 Sample[327:207] Current language: en

2010-12-23 06:48:34.063 Sample[327:207] Welcome Text: WelcomeKey


current language 의 경우 ISO639 에 따라 표기 된다.

Language

ISO 639-1

ISO 639-2

English

en

eng

French

fr

fre

German

de

ger

Japanese

ja

jpn

Hawaiian

no designator

haw

SRP


S/W 공학에서 특정 Module을 Design할때, 특정 모듈이 단 하나의 책임만을 가져야 한다는 원칙을 의미한다. 


SRP에서 말하는 책임은 "변화의 주체"이다. 


Module을 변화시킬수 있는 주체는 단 하나여야만 하는것이다.


이 원칙을 지키게 되면, Module의 분리가 일어남으로 거시적인 관점에서의 S/W Artchitecture의 복잡도가 상승하는 것처럼

보일 수도 있다. 


그러나, 특정 모듈의 Task가 단순해짐으로써, 개발효율 , 그리고 유지보수성이 증대하게 된다. 

거기다가 사실은 S/W Architecture의 복잡도 역시 줄어 들게 된다


내가 추구하는 간결함과 미니멀리즘에 잘 부합하는 내가 명심하는 원칙중 하나이다. 



App Widget 이란 Home Screen에 노출되는 miniature Application입니다.
보통 시계 어플등이 Widget 형태로 존재 하게 됩니다.

App Widget을 구현에 필요 한것들

A. 꼭 필요 한것들
1. AppWidgetProviderInfo object
- App Widget을 위한 MetaData, update frequecy 나 layout에 대한 정의.
- XML 에 정의 되어야함.
2. AppWidgetProvider
- Programming Interface
- AppWidget이 Update/Enabled/Disabled/Delete되었을때, BroadCast를 받는 Receiver역할을 한다.
3. View Layout
- AppWidget Design , Android Layout XML 로 정의한다.
B. 추가로 할 수 있는것
App Widget Configuration Activity
- AppWidget이 생성될때, User가 설정하게 할수 있는 Optional Feature

요약
Android Manifest에 AppWiget을 정의하고, AppWidgetProvider를 구현함으로써 Programming 하고,
Layout을 통해 App Widget을 디자인한다.

Manifest에 App Widget 선언하기.

앞서 언급한대로, AppWidget에는 AppWidgetProvider라는 BroadCast Receiver가 필요 합니다.

<receiver android:name="ExampleAppWidgetProvider" >
   
<intent-filter>
       
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   
</intent-filter>
   
<meta-data android:name="android.appwidget.provider"
               
android:resource="@xml/example_appwidget_info" />
</receiver>

  • AppWidget Broadcast Reciver 등록
  • MetaData Resource 설정 

AppWidgetProviderInfo MetaData


res/xml/ folder에 <appwidget-provider /> 태그를 작성한 yourappwidget-info.xml을 추가합니다.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   
android:minWidth="294dp"
   
android:minHeight="72dp"
   
android:updatePeriodMillis="86400000"
   
android:initialLayout="@layout/example_appwidget"
   
android:configure="com.example.android.ExampleAppWidgetConfigure" >
</appwidget-provider>

  • minWidth/minHeight 설정
    • 사실상 width값대로 표기되는게 아니라, 현재의 Cell Size에 따라 자동 계산된다.
    • Cell Size는 Device별로 다르고, 같은 Device일때도, Vertical이냐 Horizontal에 따라서 또 다르다.
    • ( Number of Cells ) * 74 -2 의 계산식으로 계산하여 설정하도록 한다.
  • updatePeriodMillis 설정
  • layout 명시
    • AppWidget Layout XML
  • configure(optional)
    • 유저가 AppWidget 설정시 사용될 Activity

AppWidget Layout

AppWidget의 Layout은 기본적으로 Activity용 Layout과 다를 바가 없다.
다만, AppWidget은 RemoteView를 기반으로 하고 있기 때문에, 사용할 수 있는 종류에 제약이 있다.

사용가능한 Layout Classes

사용가능한 Widget Classes

AppWidgetProviderClass 구현하기

아래 소스는 appWidget을 Click했을 때, 특정 Activity를 띄우는 AppWidget의 예이다.

public class ExampleAppWidgetProvider extends AppWidgetProvider {

   
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
       
final int N = appWidgetIds.length;

       
// Perform this loop procedure for each App Widget that belongs to this provider
       
for (int i=0; i<N; i++) {
           
int appWidgetId = appWidgetIds[i];

           
// Create an Intent to launch ExampleActivity
           
Intent intent = new Intent(context, ExampleActivity.class);
           
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

           
// Get the layout for the App Widget and attach an on-click listener to the button
           
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views
.setOnClickPendingIntent(R.id.button, pendingIntent);

           
// Tell the AppWidgetManager to perform an update on the current App Widget
            appWidgetManager
.updateAppWidget(appWidgetId, views);
       
}
   
}
}

AppWidget의 기본적인 목적은 주기적으로 화면을 구성해주서 보여주는것이다.
그럼으로 특별히 Enable/Disable를 처리할 이유가 없다면, 내가 해야 할일은 onUpdate() Method를 구현하는것 뿐이다.

이제 AppWidget을 띄울 수 있는 기본적인 개념은 알게 되었다.
 

"화면해상도별 아이콘 제작하기"

안드로이드 Device는 제각각이기때문에, 해상도별로 Icon을 각각 제작하길 권장합니다. 
보통은 "low/medidum/high" 이 3가지 Type의 아이콘을 만들어야 합니다. 

Resourcedirectory qualifiers for screen size and density. 에서 알수 있듯이 다른 Type도 존재 하기 때문에, 필요 하다면 그에 대한 고려도 필요 합니다.

안드로이드 Device의 기준 해상도는 mdpi(medium) 입니다. 
그래서 안드로이드용 Icon 을 제작할때는 아래의 절차에 따라 제작하는것이 좋습니다.

1. 먼저 BaseLine 해상도(mdpi)로 먼저 아이콘을 재작합니다.
2. AVD나 HVGA( e.g T-Mobile G1) 의 Device 에 올려서 테스트 하고. 조정합니다.
4. mdpi 용 icon 제작이 끝나면 hdpi 와 ldpi용 아이콘을 제작합니다.
  • hdpi 용 아이콘은 mdpi 용 icon을 150%정도로 upscale합니다.
  • ldpi용 아이콘은 mdpi용 icon을 75%정도로 downscale합니다.
  • 5. 해상도 별로 AVD혹은 Device에 올려서 테스트 합니다.

    Table 1. 해상도별 아이콘 사이즈 요약정리

    Icon Type Standard Asset Sizes (in Pixels), forGeneralized Screen Densities
    Low density screen (ldpi) Medium density screen (mdpi) High density screen (hdpi)
    Launcher 36 x 36 px 48 x 48 px 72 x 72 px
    Menu 36 x 36 px 48 x 48 px 72 x 72 px
    Status Bar 24 x 24 px 32 x 32 px 48 x 48 px
    Tab 24 x 24 px 32 x 32 px 48 x 48 px
    Dialog 24 x 24 px 32 x 32 px 48 x 48 px
    List View 24 x 24 px 32 x 32 px 48 x 48 px
     
    Android R Class는 AndroidManifest를 기준으로 자동 생성되는 Class 파일이다.

    고로, AndroidManifest에서 Package를 변경할 수 있다.

    <manifest></manifest> 를 보면,

    packge라는 attribute 가 있고, 그 값을 수정해주고, 저장하면 자동으로 변경된다. 

    단, R Class를 사용하는 소스의 import는 알아서 정리해줘야 한다. 




    오늘 오래간만에, Android New Project를 만들려고하는데,

    proguard.cfg 가 없다는 에러가 발생 ;ㅂ;

    이건 도 뭐야 하면서 짜증내는데,, stackoverflow.com 을 보니까, Android SDK Tool을 Upgrade해보라고 했다. 


    update 하니까 잘된다 ㅎㅎ



    EditMode 일때는 TableView의 TableViewCell이 Touch가 안됩니다. 


    즉, didSelectRowAtIndexPath: Delegate Method가 호출이 안되는것이죠. 


    그런데, EditMode일때도, Touch 즉 TableView Selection이 동작했으면 하는 상황이 있다면??

    이건 생각보다 쉽습니다.


    UITableView Header 파일을 보면

    allowsSelection 과 allowsSelectionDuringEditing이라는 옵션이 있습니다.

    allowsSelection 은 기본인 Selection에 대한 옵션이고,
    allowsSelection의 경우 Edit Mode에 대한 Selection입니다. 

    근데, 이 녀석이 Default=NO 인 까닥에, Edit Mode에서 didSelectRowAtIndexPath: Delegate Method가 호출 되지 않은것이죠.

    그냥
    [tableview setAllowsSelectionDuringEdit:YES]를 한번 호출 해주면됩니다.





    Twitter는 회원 인증과 OAuth 와 XAuth를 지원합니다. 
    사실 XAuth로 인증 하는것이 Mobile에서 보편화된 방법인데, XAuth는 ID/Password를 3rd-Party Application에서 입력 받기에,  
    개인적으로는 OAuth를 선호 합니다. 

    Twitter에서 OAuth는 oauth_verifier를 Pin Code라는 개념으로, 유저에게 HTML 화면으로 노출시키고, 그것을 입력하도록 하고 있습니다. 
    "휴대폰 인증번호" 쯤으로 생각하면 될듯합니다. 

    사용자에게 입력하게 한다. 사용자는 이미 아이디와 비밀번호를 입력했는 데 또 입력하게 해야 한다니, 
    별로 좋지 않군요. 

    오늘의 목표는 저 Pin Code값을 자동으로 가져오는 것입니다 ! 

    그래서 HTML 을 Parsing해서 가져와보자고 생각을 했습니다. 

    Pin 값이 <div id="oauth_pin"> 으로 되어 있어서, HTML DOM Element Parsing으로 가져 올수 있을꺼 같다는 생각을 했습니다.

    그러나..

    Android WebView에 HTML 을 가져 오는게 존재 하지 않습니다 ;ㅂ;

    그래서 WebView에 Javascript 를 집어 넣어서, Java Code를 호출 하여, dom의 값을 넣기로 했습니다. 


    class MyJavaScriptInterface  

    {  

        public void showHTML(String pin) throws TwitterException  

        {  

        if(pin.length() > 0 ) {

        Log.i("Javascript Interface","pin = " + pin);

        AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, pin);

        Log.i("Javascript Interface", "AccessToken = " + accessToken);

        }

        else {

        Log.i("Javascript Interface","get pin failed...");

        }

        }  

    }  

     webview.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");


    이런식으로 Java Code와 연결된, Javascript Interface를 WebView에 삽입 할수 있습니다. 

    view.loadUrl("javascript:window.HTMLOUT.showHTML(document.getElementById('oauth_pin').innerHTML);");  

    view.loadUrl("javascript:window.HTMLOUT.showHTML(document.getElementsByTagName('code')[0].innerHTML);");

    WebViewClient의 onPageFinisehd() Method를 Override하여, 삽입한 Javascript를 실행해주면
    Page가 로딩 될때마다, 호출되고,oauth_pin 값을 얻는 순간이 인증이 끝난 순간이됩니다. 

    그것을 통해서 AccessToken을 가져오면 Twitter OAuth 인증은 끝난 셈이죠. 

    Java Code내에서 끝나지 않고, Javascript 를 통한다는게 좀 마음에 안드는 방식이긴 합니다. 
    더 좋은 방법을 알게 되면 후딱 후딱 넘어가야죠. 어쨌든 잘 동작합니다. 


    iPhone 사용하시다 보면, 다들 이런 경험 있으시죠? ^^

    iOS App Store에서 App.을 다운로드 받을때, 20 MB 이상인 녀석들은 3G 인터넷을 통해 다운로드 받을 수 없습니다. 
    App Download에 제한이 걸리게 되면, App 판매량에도 분명 영향을 미칠것입니다.

    자 그럼 어떻게 이 뚱뚱한 App 녀석을 다이어트 시켜야 할까요?

    최근에 알게된 것중에 하나 인데, 소개 해보려고 합니다. 
    Stretchable Image 즉, 확대 가능한 이미지를 사용함으로써, Image Size를 줄이고, 궁극적으로는 Application Size를 줄일 수 있습니다.
    Android개발을 해보신 분들이라면, 9 patch Image라는 녀석을 아실텐데요, 그녀석과 흡사하지만 덜 파워풀한녀석입니다 ㅎ

     

    실험 대상이 될 Image는 바로 요녀석인데요, 요녀석은 40x24 입니다. 
    근데 저는 이녀석을 40x24로도 사용하고, 240x24 로도 사용하고 싶습니다.

    가장 간단한 방법은 이녀석을 40x24 버전과 240x24버전을 따로 준비 하는것인데, 이러면서 App.이 뚱뚱해지기 시작합니다. 

    그럼 어떻게 해야 하냐 ? 범인, 아니 정답은 여기 있습니다 !

    - (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;


    이녀석을 이용하면, 일단 UIImage를 Stretchable하게 바꿔줍니다. 


    위의 스킨 샷을 보시면, Stretchable한 Image를 이용하지 않고 그냥 UIImageView의 크기만 늘리게 되면, 
    두번째 나오는 녀석처럼 쭈욱 늘어나서, 못생긴 녀석이 됩니다. 
    그러나 UIImage를 Stretchable한 상태로 만들어 주면 짜잔 , 3번쨰 녀석 처럼 매끈하게 나옵니다. 


    UIImage *img = [[UIImage imageNamed:@"image.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];

    [stretchableView setImage:img];


    예제 코드는 위와 같은데요, 
    leftCap/topCap 값을 줄수 있어서, 값에 따라 Image를 upscale할때, 특정 픽셀들은 남기고, 아래 값들만 upscale시키게 됩니다. 

    애석하게도, 위의 방법은 Interface Builder에서는 할 수 있는 방법이 없기 때문에, 
    UIimageView에 Image를 세팅하는것을 소스코드 레벨에서 따로 해줘야 합니다. 

    Apple이녀석들은 이런 괜찮은걸 만들어 놨으면, InterfaceBuilder에서도 쓸 수 있게 해줘야지 ;ㅂ; 
    친구 녀석 말따라, Apple 애들도 IB를 잘 안써서 그런가 봅니다.


    Sample Project 첨부해 드리니, 살펴 보실 분들은 한번씩 살펴 보시기 바랍니다. 

    어떻게, 당신의 App은 다이어트에 성공하셨나요? ^^




    GDB 라는 엄청나게 강력한 도구를 C언어를 배울때 부터 알고 있었고, 몇번 쓰려고 했었지만, 
    Makefile + vim 을 사용하던 시절에는 그냥 귀찮아서, printf로 작업한적이 많았고, 
    GDB를 그냥 Core Dump의 Back Trace용도로만 사용했었습니다. 

    이번에 작업을 하면서 BreakPoint를 찍어서 GDB를 활용하려고 다시 시도 해보려고 합니다.
    어쩌면 다시 귀차니즘에 printf(iOS에선 NSLog)로 돌아 갈지도 모르지만, 말이죠 ㅎㅎ

    XCode에서 BreakPoint를 찍는건 상당히 간단한데요,
    Code Editor의 Line Number쪽을 클릭해주면 됩니다. 

    그런후에, Build & Run이 아니라 Build & Debug를 실행시켜주면, GDB와 함께 App.이 실행 됩니다. 



    Build & Debug를 한후에 GDB를 켜보면 이런식으로 화면이 나오는데요

    특정 BreakPoint가 어떤 순서로 찍혀 져있는지, Application Cycle도 알수 있고,
    구지 NSLog등으로 찍어 주지 않아도, 

    변수, 주소 값 등등을 손쉽게 Navigation할 수 있습니다. 

    별로 어려운것도, 아니지만, 상당히 강력하지만 또 번거롭기도 한 녀석인데요 
    이번에는 좀더 전투적으로  사용해볼까 합니다 

    + Recent posts