iOS4 로 업글되면서 XCode도 같이 업데이트했더니..어라?
3.1이 안보인다.
그럼 나같이 아직도 3.1 쓰는 사람은 어떻게 개발하라고?

개발 시 필요한 Framework가 달라졌다. 그래서 컴파일도 안되고..ㅋㅋ
사용자 삽입 이미지

위와 같이 AVFoundation.framework는 3.x에는 없다. 이럴 경우 Role을 Required에서 Weak로 변경해 준다.

그리고 프로젝트 정보에서 배포를 3.x로 변경한다.
사용자 삽입 이미지

이렇게 하면 3.x로 컴파일도 되고, 배포도 가능하다.
단 소스에서 버전별로 처리를 해야 하는 부분이 있다면,

[[[UIDevice currentDevice] systemVersion] doubleValue] < 3.2

위와 같이 시스템의 버전을 확인한 후에 코딩이 필요하다.
근데 진짜 삽질 많이 해야 한다. iOS4가 되면서 API가 많이 달라져서..

WIPI도 그랬는데, Android 도 그렇고..젠장 이제는 iPhone까지
하위버전에 대한 100% 호환을 안해주면 어쩌란 말이냐?



Posted by 피의복수

iOS4가 나오면서 조금씩 달라지는 API들.
역시 Upgrade에 장사없다고, 하위버전에 대한 호환이 안되는군. OS자체를 다 바꿔야 하니..ㅋㅋ

일단 아래와 같이 3.2를 기준으로 하위 버전과 상위버전에 대한 처리를 다르게 해 본다.

/**
 * Video Play
 * @param data : 인식된 data
 */
- (void) playMovie: (NSString *) dataValue
{
    NSString *movieURL;
   
    movieURL = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp4"];

    /// iPhone Device 버전에 따라 동영상 Play
    if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 3.2) {
        /// MPMoviePlayerViewController 초기화
        MPMoviePlayerViewController *playerView = [[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:movieURL]] autorelease];
        playerView.view.backgroundColor = [UIColor blackColor];
        playerView.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
       
        /// 동영상 끝났을때 호출
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(playbackDidFinish:)
                                                     name:MPMoviePlayerPlaybackDidFinishNotification
                                                   object:playerView.moviePlayer];
        /// Play the movie
        [self presentMoviePlayerViewControllerAnimated:playerView];
    }
    else if    ([[[UIDevice currentDevice] systemVersion] doubleValue] < 3.2) {
        /// MPMoviePlayerController 초기화
        MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:movieURL]];
        moviePlayer.initialPlaybackTime = 0;
        moviePlayer.scalingMode = MPMovieScalingModeAspectFill;
        moviePlayer.movieControlMode = MPMovieControlModeDefault;
        /// 동영상 끝났을때 호출
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(playbackFinishedCB:)
                                                     name:MPMoviePlayerPlaybackDidFinishNotification
                                                   object:moviePlayer];
        /// Play the movie
        [moviePlayer play];
    }
}



/**
 * when the movie is finished
 */
- (void) playbackFinishedCB:(NSNotification *) aNotification
{
    if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 3.2) {
        MPMoviePlayerController *player = [aNotification object];
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:MPMoviePlayerPlaybackDidFinishNotification
                                                      object:player];
        [player stop];
        [self dismissMoviePlayerViewControllerAnimated];
    }
    else if ([[[UIDevice currentDevice] systemVersion] doubleValue] < 3.2) {
         MPMoviePlayerController *player = [aNotification object];
       
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                              name:MPMoviePlayerPlaybackDidFinishNotification
                                              object:player];
        [player stop];
        [player release];
    }
}


Posted by 피의복수

MAC에 있는 iWeb으로 사이트를 만들면 곤란한 경우가 발생한다.
이게 다른데서 만든 것을 불러올 수가 없다. 오직 자신의 MAC에서 만들어진 것을 사용할 수 밖에 없는데, 지금부터 내가 만든 사이트를 다른 사람이 볼 수 있도록, 그리고 다른 사람이 만든 사이트를 내가 가져올 수 있도록 작업을 해 본다.

헐^^도대체 어디에 저장을 하는겨?

MAC의 Application은 대부분 사용자계정의 Library에 데이터를 저장한다.
Library > Application Support 에 가면 Application이 사용하는 또는 저장한 데이터들이 있다.
cd iWeb 에 가서 안의 내용을 보면 Domain.sites2라는 directory가 보일 것이다.
그 안에 모든 내용이 있다고 보면된다.

Domain1.sites2라는 directory를 만들어서 모든 내용을 여기에 복사한 후 다른 사용자에게 배포하면 된다.

참 쉬죠~~

Posted by 피의복수

Windows에서 사용할 수 있는 VisualStudio의 가격이 만만치 않다.
그래서 이번에 큰 맘먹고 Windows에서 개발을 해보기로 하겠다. ㅋㅋ

1. cygwin이 필요하다. ==> http://www.cygwin.com/setup.exe 다운받으시고
2. cygwin을 설치하자
  setup.exe를 설치하면 다음과 같은 화면이 나온다. 놀라지 마라.그냥 install하지 말고 local에 저장하자.
  다음으로 넘어가면 이제 다운로드를 할 경로를 설정하고 환경을 맞춘다음 사이트를 선택해야 한다. kaist도 있지만 난 그냥 일본 사이트에서 다운로드 했다.

3. 다운로드가 완료되면 다시 한 번 setup.exe를 실행한 후 이번에는 설치하자. 

4. 자 이제 설치가 끝났다.
Posted by 피의복수

아마도 카메라 영상의 이미지에서 Marker를 추출하고 인식하는 것이 가장 우선되어야 할 것이다.
다만 서비스에 따라서 이 Marker를 Tracking할 것인가 아니면 단순히 인식만 할 것인 가가 이슈가 되겠다.

간단한 샘플
http://technotecture.com/content/sstt-visualizer-augmented-reality-demo
위 URL에서 제공하는데로 하면 간단한 샘플을 볼 수 있다.

우선적으로 iPhone에서 사용할 수 있는 ARToolkit을 먼저 알아보자. (구현하는 방식은 알겠지만 헉....언제 만들어?)

http://sproke.blogspot.com/2009/11/augmented-reality-resources-for.html
나름 ARToolkit의 종류에 대해 정리를 잘 해놓은 것 같다.
 근데 어떻게 쓸까?

아래 사이트에서는 Windows환경에서 OpenCV를 이용한 마커 추출/검증 및 동영에 대한 강좌를 제공한다.
http://martinblog.tistory.com/category/%5B%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%20%5D/%EA%B0%95%EC%A2%8C

.Net 기반이기는 하지만 얼 추 원리를 설명함.
http://whatisthat.co.kr/tag/AR


전체적으로 기본적인 원리는 카메라 영상에 대한 이진화를 한 후 각 영상에 대한 라벨링 후 마커를 추출하는 방법을 사용한다. 또한 추출된 마커의 꼭지점을 계산하여 해당 영역에 동영상이나 3D 같은 컨텐트를 출력하게 하는 방법을 사용한다.


Posted by 피의복수



android.os.Build.MODEL 을 확인해 보면 "sdk"라고 나올 경우는 에뮬레이터 환경

기타 제조사등의 정보도 android.os.Build를 확인하면 가능함.

SystemProperty를 통해서 알수 있는 방법도 확인이 필요함.
(SystemProperties는 private 영역으로 접근 불가함.)
Posted by 피의복수

자 이제 MapView를 통해 구글이 제공하는 지도를 볼 수 있다는 것을 알았고, 그 위에 마커를 올려줄 수 있다는 것을 확인했다.
근데 Map 예제를 보면서 왜 지도가 세계지도로 나올까? 난 이게 참 궁금했다.
확인해보니 Map과 관련된 Control을 관리해 주어야 하는 구만.
그럼 지금부터는 진짜 지도를 보자.

MapView를 사용하기 위해서는 manifest.xml에
<uses-library android:name="com.google.android.maps" /> 과
<uses-permission android:name="android.permission.INTERNET" /> 를 추가해 주어야 한다는 것을 이미 배웠다.

이런 식으로 GPS를 확인하기 위해 다음과 같은 permission이 추가되어야 한다.
 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>   
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

하나씩 설명하기 어려우니 작성한 소스를 올린다.

package com.bloodlee;

/**
 * MapView를 위한 Sample을 작성합니다.<br>
 * 1. 현재 GPS 정보를 조회하여 Map을 그립니다.<br>
 * 2. GPS 정보가 변경되면 해당 위치를 다시 그립니다.<br>
 * 3. 혠 wjdqh
 * @author bloodlee
 */

import java.io.IOException;
import java.util.List;
import java.util.Locale;

import com.bloodlee.R;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

import android.content.Context;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class MapviewSample extends MapActivity implements LocationListener{

 MapController mc;
 
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        // MapView 생성
        MapView mapView = (MapView) findViewById(R.id.mapview);
        // MapView 하단에 Zoom 생성
        mapView.setBuiltInZoomControls(true);

        // LocationManager를 구한다.
        LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        // 현재 시스템의 LocationManager에서 활성화된 Provider를 조회한다.
        List<String> providers = locManager.getProviders(true);
        for(int i=0; i < providers.size();i++)
         Log.i("Providers in LocationManager",providers.get(i));

        // Provider의 조건으로 최적의 Provider를 조회한다.
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        criteria.setAltitudeRequired(false);
        criteria.setBearingRequired(false);
        criteria.setCostAllowed(true);
        criteria.setPowerRequirement(Criteria.POWER_LOW);
        String provider = locManager.getBestProvider(criteria, true);
     Log.i("BestProvider",provider);

        // Provider에 저장된 최종 위치 정보를 조회한다.
     Location loc = locManager.getLastKnownLocation(provider);
        GeoPoint p = null;
        // Provider를 통해 현재 위치를 조회한다.
        if(loc != null){
         int lat = (int)(loc.getLatitude()* 1E6);
         int lng = (int)(loc.getLongitude() * 1E6);
         p = new GeoPoint(lat, lng);
         Log.i("GPS Information","[" + lat + "],[" + lng + "]");
        }
        else{ // 현재 위치를 조회할 수 없는 경우 기본값(명동)을 설정한다.
         Log.i("GeoPoint","Location is null");
         p = new GeoPoint(37559978,126985806);
        }
        Log.i("Address",getAddress(p));
       
        mc = mapView.getController();
        mc.animateTo(p);
        // Zoom Level을 설정한다. 1 ~ 21까지. 21이면 최대 확대
        mc.setZoom(15);
       
        // GPS 정보가 변경을 조회한다. GPS가 변경되면 onLocationChanged 호출
     locManager.requestLocationUpdates(provider, 1000L, 500.0f, this);
     
    }
   
    /**
     * LocationListener를 통해 GPS 정보가 변경되는 경우 호출
     */
 public void onLocationChanged(Location loc) {
  if (loc != null) {
         int lat = (int)(loc.getLatitude()* 1E6);
         int lng = (int)(loc.getLongitude() * 1E6);

   GeoPoint p = new GeoPoint(lat, lng);
   mc.animateTo(p);
         Log.d("onLocationChanged","[" + lat + "],[" + lng + "]");
            Log.i("Address",getAddress(p));
  }
 }

 public void onProviderDisabled(String provider) {
  // required for interface, not used
 }
 
 public void onProviderEnabled(String provider) {
  // required for interface, not used
 }
 
 public void onStatusChanged(String provider, int status, Bundle extras) {
  // required for interface, not used
 }
 
    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }
   
    /**
     * 취치 정보로 해당 주소 정보를 조회한다.
     */
    private String getAddress(GeoPoint p){
        // 주소 정보를 조회합니다.
     // 한국 주소가 잘 안나오는 것 같다.
        Geocoder geocoder = new Geocoder(this, Locale.getDefault());
     StringBuilder sb = new StringBuilder();
        try{
         List<Address> addrList = geocoder.getFromLocation((double)(p.getLatitudeE6() / 1E6),
                       (double)(p.getLongitudeE6() / 1E6),
                                                         10);
            if (addrList.size() > 0) {
             Address addr = addrList.get(0);

             /* 잘 안나옴.
             for (int i = 0; i < addr.getMaxAddressLineIndex(); i++)
              sb.append(addr.getAddressLine(i)).append(", ");
             Log.i("addr.getAddressLine(i))", sb.toString());

             sb.append(addr.getAdminArea()).append(", ");
             if(addr.getAdminArea() != null)
             Log.i("addr.getAdminArea()", addr.getAdminArea());
             sb.append(addr.getSubAdminArea()).append(", ");
             if(addr.getSubAdminArea() != null)
             Log.i("addr.getSubAdminArea()", addr.getSubAdminArea());
             sb.append(addr.getPremises()).append(", ");
             if(addr.getPremises() != null)
             Log.i("addr.getPremises()", addr.getPremises());
             */
             sb.append(addr.getFeatureName()).append(", "); // 번지
             sb.append(addr.getLocality()).append(", "); // 시, 군
             sb.append(addr.getPostalCode()).append(", ");
             sb.append(addr.getCountryName());

             Log.i("Address", sb.toString());
             Toast.makeText(MapviewSample.this, sb.toString(), Toast.LENGTH_LONG).show();
            }

        }catch(IOException ioe){
         Log.e("Exception on getting Geocoder", ioe.getLocalizedMessage());
        }
     return sb.toString();
    }
}


 

Posted by 피의복수

1. aapt(Android Asset Packaging Tool)
  - 안드로이드 패키징 Tool
  - eclipse의 ADT를 사용하고 있다면 굳이 볼 필요는 없음.

2. adb(Android Debug Bridge)
  - emulator instance나 device의 상태를 관리하는 Tool
  - Clietn / Server로 구성(Server는 emulator나 Device의 daemon과 client와의 Communication을 관리한다.
  - Client를 실행하면 Server의 활성화를 확인하며, 활성화되지 않은 경우 Server를 시작한다.
  - Server는 local TCP port 5037에 bind한다.
  - Server는 emulator와 Device에서 사용하는 5555에서 5585까지의 port를 스캔한다.
  - emulator와 Device은 한쌍의 순차적 port를 사용하는데, emulator가 5554를 사용하면 adb는 5555를 사용한다.

  1) emulator/Device Instance조회
    > adb devices
[serialNumber] [state] -- state: offlline or device

List
of devices attached
emulator
-5554  device
emulator
-5556  device
emulator
-5558  device 
 
  2) 특정 emulator/Device에 Command 실행
    > adb -s <serialNumber> <command>
adb -s emulator-5556 install helloWorld.apk

  3) Application 설치
    > adb install <path_to_apk>

  4) Forwarding Port
    > adb forward tcp:<port> tcp:<port>

  5) copy file to or from emulator/device instance
    > adb pull <remote> <local>

    > adb push <local> <remote>

  6) 기타 Command는 http://developer.android.com/guide/developing/tools/adb.html#commandsummary 참조



 
Posted by 피의복수

Home Screen을 사용하는 Widget을 만드는 방법을 정리해 보자
Widget을 만들려면 다음과 같은 3가지가 필요하다.
1. AppWidgetProviderInfo
  Widget의 metadata를 정의한다. AppWidgetProvider class, Widget layout 그리고 업데이트 주기등을 XML에 정의한다.
/res/xml 에 해당 xml로 정의하면 된다.

2. AppWidgetProvider
  실제 Widget Application.
업데이트 등 처리

3. View Layout 
  초기 layout을 정의하며, 이는 optional

또한 Widget을 설정하기 위한 Widget Configuraion을 정이할 수 있다. (반드시 있어야 하는 것은 아님)

본격적으로 Widget을 만들어 볼까? 간단하게 Home에 시간을 나타내는 Widget을 만들어 본다.
(나중에 안 사실이지만 Time같이 초단위로 화면이 업데이트 되어야 하는 것들은 Widget으로 만들면 안된다. 업데이트 주기의 minimum값이 있는 것 같다.더 확인해봐야 겠지만 거의 30분정도...^^ 또 삽질했다)
1. Mainfest에 Widget Application 선언   
<application android:icon="@drawable/icon" android:label="@string/app_name">
        <receiver android:name=".TimeWidget" android:label="Timer Widget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/time_widget_provider" />
        </receiver>
    </application>
    <uses-sdk android:minSdkVersion="5" />
  TimerWidget이라는 Class를 만들로 providerInfo는 /xml/time_widget_provider.xml을 사용하기로 정의한다.
그리고 WidgetProvider는 receiver의 기능을 하기 때문에 activity가 아닌 receiver로 정의하고, 안드로이드 예제처럼 별도의 receiver를 만들지 않고 WidgetProvider에서 receiver의 onReceive() method를 override하여 구현한다.

2. AppWidgetProviderInfo Metadata 추가하기
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="146dp"
    android:initialLayout="@layout/main"
    android:updatePeriodMillis="1810000"
    android:minHeight="144dp"/>
초기 화면의 layout은 /layout/main.xml을 사용한다. 그리고 사이즈를 정의한다. 업데이트 주기는 30분 이상으로 설정한다.

3. Widget layout
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/LinearLayout01"
    android:layout_height="146dp"
    android:layout_width="144dp"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/widget_timeview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center_vertical|center_horizontal"
        android:textColor="@android:color/black"
        android:textSize="12pt"
    />
</LinearLayout>

  Time을 화면에 그리기 위한 TextView를 정의한다.

4. AppWidgetProvider를 상속한 class 생성

public class TimeWidget extends AppWidgetProvider {

    java.text.DateFormat timeformat = SimpleDateFormat.getTimeInstance( SimpleDateFormat.MEDIUM, Locale.getDefault() );

 @Override
    public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds )
    {
        updateTime( context, appWidgetManager);
    }
   
 @Override
    public void onReceive(Context context, Intent intent) {
        updateTime( context, AppWidgetManager.getInstance(context););
    }

    public void updateTime( Context context, AppWidgetManager appWidgetManager)
    {
        RemoteViews remoteViews1;
        ComponentName watchWidget;
       
        remoteViews1 = new RemoteViews( context.getPackageName(), R.layout.main );
        watchWidget = new ComponentName( context, WatchWidget.class );
        remoteViews1.setTextViewText( R.id.widget_timeview, timeformat.format( new Date()));
        appWidgetManager.updateAppWidget( watchWidget, remoteViews1 );
    }
   
}

 emulator에서 확인해 보니 실제 WIdget이 화면에 설정되면 onReceive() method가 호출되고 업데이트 주기가 되면 onUpdate() method가 호출됨을 확인하였다.

5. Widget 설정하기
  이렇게 만들어진 Widget을 어떻게 단말에 설정할까? emulator의 menu > add > widget을 선택하면 설정할 수 있는 widget list가 나온다 그리고 선택하면 된다.
근데 지울때는? 이거땜에 구글링을 했다.  Widget을 한 2초 정도 누르고 있으면 하단에 휴지통이 생긴다. 그럼 선택한 widget을 휴지통에 드래그하면 삭제된다.

주의) 삽질하고 알아낸 건데 업데이트 주기는 30분 이상으로 설정되어야 하는 것 같다.

Posted by 피의복수

1. emulator
  eclipse를 통해 구동하다보면 가끔씩 오류가 발생한다.
이럴 때는 당황하지 말고 SDK가 설치된 directory로 이동하여 emulator를 직접 구동해 보자

tools >emulator -avd avd_name 으로 emulator를 실행한 후에 실행 option에서 실행한 avd_name을 선택하면 오류가 없다.
참고로 자세한 사용법은 tools > emulator -help를 확인해 보셈^^
기존에 설치된 것을 초기화하기 위해서는 -wipe-data option을 추가해서 실행하면 되겠다.
Widget을 삭제하려면 드래그해서 휴지통에 넣으라는데....에뮬에서는 휴지통을 못 찾겠네.
==> 위젯을 좀 오래 클릭해보면 하단에 휴지통이 나타난다. 이 후 드래그해서 넣으면 끝^^

2. Log
  Java의 log4j처럼 유사한 Log를 제공한다.
Log.i(Information), Log.d(Debug)등을 활용하면 된다. 물론 System.out.println도 가능하다.
이렇게 남겨진 로그는 DDM을 이용해 확인해 보면 된다.
DDM은 어디 있을까?

tools > ddms  참 쉽죠~~잉

3. SDK Update
  eclipse에서 SDK Update하는 경우에는 발생하지 않는데, SDK의 "SDK Setup.exe"를 통해 Update를 할 경우 https://xxxxx/repository.xml에 대한 접근 오류가 나는 경우가 있다. (이것 땜에 또 삽질)
Window 사용자라면 사용자 계정 directory로 가봐라. ./android라는 directory가 보일 것이다.
그 밑에 보면 androidtool.cfg 가 있는데 여기에 sdkman.force.http=true 추가하면 그 뒤로는 접속이 될 것이다.
게속 삽질중...^^

Posted by 피의복수

BLOG main image
일에 필요한 자료 by 피의복수

카테고리

분류 전체보기 (40)
프로그램이야기 (38)
끄적끄적 (1)
취미 (0)
서비스이야기 (1)
빅데이터 (0)

최근에 올라온 글