2010년 10월 5일 화요일

android JNI을 이용하여 serial 읽어 오기

http://developer.android.com/sdk/ndk/index.html

위에 홈페이지에서 ndk를 다운 받고 적당한 디렉토리에 압축을 푼다.

eclipse에서 프로젝트를 하나 생성한다.

프로젝트 생성시 반드시 minSdkVersion="3"을 추가 해야 한다.

AndroidManifest.xml에 추가한다.

project 폴더에 jni 폴더를 만들다.

ndk/sample/ 폴더에 예제를 참조 하여 Android.mk를 생성한다.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := gps_serial
LOCAL_SRC_FILES := gps_serial.c \
jni_serial.c

LOCAL_LDLIBS := -ldl -llog

include $(BUILD_SHARED_LIBRARY)



jni 폴더에 소스를 생성한다.

gps serial를 읽어 오는 gps_serial.h/c를 생성한다.

//---------------gps_serial.h-----------------------//
#ifndef GPS_SERIAL_H
#define GPS_SERIAL_H

char* read_serial();

#endif //GPS_SERIAL_H



//---------------gps_serial.c-----------------------//
#include
#include
#include
#include
#include
#include

#define GPS_SERIAL_NODE "/dev/ttyACM0"
#define READ_COUNT 256
#define LOG_TAG "GPS Serial"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

int fd = -1;
char buf[READ_COUNT];

int openGPS()
{
LOGI("oepnGPS()");

if (fd = open(GPS_SERIAL_NODE, O_RDONLY) >= 0) {
return fd;
} else {
LOGI("failed to oepn!");
return fd;
}
}

void closeGPS()
{
LOGI("closeGPS()");

if (fd)
close(fd);

fd = -1;
}

char* read_serial()
{
int read_count = 0;
int idx = 0;

memset(buf, 0x00, READ_COUNT);

if (fd == -1)
goto out;

read_count = read(fd, buf, READ_COUNT);
LOGI("count %d", read_count);
LOGI("%s", buf);

LOGI("read success!!");

out:
return buf;
}

여기까지는 일반적인 linux 프로그램이다. 단 printf로 메세지를 확인 할 수 없기 때문에 __android_log_print()를 이용하여 log를 확인 해야 한다.

그리고 java와 c library를 연결 할 jni 함수를 만든다.

//---------------jni_serial.c-----------------------/

#include
#include

#include

void Java_com_hardkernel_gpsserial_GPSSerialActivity_openGPS(JNIEnv* env, jobject obj)
{
openGPS();
}

jstring Java_com_hardkernel_gpsserial_GPSSerialActivity_readGPS(JNIEnv* env, jobject obj)
{
return (*env)->NewStringUTF(env, read_serial());
}

void Java_com_hardkernel_gpsserial_GPSSerialActivity_closeGPS(JNIEnv* env, jobject obj)
{
closeGPS();
}

JNI 함수의 이름 규칙은 Java_ 이후에 package name 그리고 jni를 호출 할 class 이름 그리고 함수 이름을 붙여 주면 됩니다.

Java_com_hardkernel_gpsserial_GPSSerialActivity_openGPS
_____패키지이름_______________class 이름_______함수 이름

이제 ndk를 이용하여 build를 해 보자.

jni 폴더에서 [android-ndk가 설치된 폴더]/ndk-build 명령으로 build를 한다.
하고 나면 아래의 그림과 같이 libs 폴더가 생성된다.


//---------------GPSSerialActivity.java-----------------------/
package com.hardkernel.gpsserial;

import com.hardkernel.gpsserial.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

public class GPSSerialActivity extends Activity {
private TextView mTv;
private Thread mReadThread = null;
private String mString;
boolean mRunning = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTv = (TextView)findViewById(R.id.gps_textview);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
if (mReadThread != null && mReadThread.isAlive()) {
Log.w("codewalker", "onPause()");
mRunning = false;
mReadThread = null;
}
closeGPS();
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
Log.w("codewalker", "onResume()");
super.onResume();
openGPS();
if (mReadThread == null) {
mRunning = true;
mReadThread = new Thread(new Runnable() {
public void run() {
while (mRunning) {
try {
Thread.sleep(500);
} catch (InterruptedException ignore) {
}
mString = readGPS();
mHandler.sendMessage(mHandler.obtainMessage());
}
}
});
mReadThread.start();
}
}

@Override
protected void onDestroy() {
if (mReadThread != null && mReadThread.isAlive())
mReadThread.destroy();
// TODO Auto-generated method stub
super.onDestroy();
}
Handler mHandler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
mTv.setText(mString);
};
};

public native String readGPS();
public native void openGPS();
public native void closeGPS();

static {
System.loadLibrary("gps_serial");
}
}

jni 함수를 위와 같이 등록해 주고 load해 주는 코드를 추가 한다.

2010년 8월 18일 수요일

odroid에 T-Login(LM-629HUL) 사용하기

이전에 HSDPA USB Modem(LM-629HUL )을 linux에서 사용하기 위한 내용을 posting 하였습니다.
linux에 먼저 작업한 이유는 odroid-t에 t-login을 사용하기 위해서 입니다.

그래서 이번 내용은 이전에 올렸던 linux 작업했던 내용을 이어서 android에 HSDPA USB Modem porting 방법을 posting합니다.

x86 usb-modeswitch는 libusb-0.1 버전을 사용하였습니다.
하지만 libusb android를 검색하면 libusb-1.0 버전이 있습니다.
그리고 libusb-1.0에 libusb-0.1버전을 사용할 수 있는 libusb-compat가 있습니다.
둘다 android용으로 library가 존재 합니다.
하지만 external에 넣고 빌드 하여 libusb.so와 libusb-compat.so를 만들어지만 dynamic link가 되지 않아서 configure에 --enable-static을 둘다 주고 usb_modeswitch도 static으로 만듭니다.
usbutils 역시 동일하게 static으로 만들면 lsusb를 통하여 usb mode switch가 되었는지 확인이 가능합니다.

여기서부터 추가
external 밑에 아래 세 폴더를 복사 합니다.
monaka-libusb-android-b35de21
monaka-libusb-compat-android-85dc44d
usb-modeswitch-1.1.3

monaka는 Android.mk가 있습니다.

usb-modeswitch-1.1.3에 Android.mk는 아래와 같습니다.

1 ifeq ($(TARGET_ARCH),arm)
2
3 LOCAL_PATH:= $(call my-dir)
4 include $(CLEAR_VARS)
5
6 LOCAL_SRC_FILES:= \
7 usb_modeswitch.c
8
9 LOCAL_SHARED_LIBRARIES := \
10 libusb-compat libusb
11
12 LOCAL_C_INCLUDES := \
13 $(LOCAL_PATH)/include
14
15 LOCAL_CFLAGS := \
16 -Iexternal/monaka-libusb-compat-android-85dc44d/libusb -pthread -rt
17
18 LOCAL_MODULE:= usb_modeswitch
19
20 include $(BUILD_EXECUTABLE)
21
22 endif

이렇게 추가하면 odroidt-img/system/bin/usb_modeswitch와 odroidt-img/system/lib/libusb.so, libusb-compat.so가 생깁니다.

그리고 추가로 external/monaka-libusb-android-b35de21/examples을 컴파일 하면 lsusb가 생깁니다.
Android.mk는 아래와 같습니다.

1 ifeq ($(TARGET_ARCH),arm)
2
3 LOCAL_PATH:= $(call my-dir)
4
5 include $(CLEAR_VARS)
6
7 LOCAL_SRC_FILES:= \
8 lsusb.c
9
10 LOCAL_C_INCLUDES += $(LOCAL_PATH)/android \
11 external/monaka-libusb-android-b35de21
12
13 LOCAL_SHARED_LIBRARIES := \
14 libusb
15
16 LOCAL_CFLAGS := \
17 -pthread -rt
18
19 LOCAL_MODULE:= lsusb
20
21 include $(BUILD_EXECUTABLE)
22
23 endif

이렇게 위에서 언급한 static문제는 해결 되었습니다.
추가 끝

다음 단계로 커널에 usb file system이 올라 가야 합니다. kernel에 usb file system을 추가 하시고
odroid-t는 ramdisk를 사용함으로 vendor/sec/android/conf/init_ramdisk.rc 파일에 usbfs를 mount하도록 추가 합니다.

mount usbfs none /proc/bus/usb

다음 부터는 아래 주소에 있는 x86 emulator에서 HSDPA를 연결한 내용을 참고하였습니다.

내용을 간략하게 설명하면 host linux에 modem을 연결하면 ttyUSB가 생성되고 여기서 command port인 ttyUSB3을 android의 ttyS1으로 연결한다는 내용입니다.

vendor/sec/odroidt/system.prop 파일을 아래와 같이 수정 합니다.

1 #
2 # system.prop for odroidt
3 #
4
5 rild.libpath=/system/lib/libreference-ril.so
6 #codewalker
7 #rild.libargs=-d /dev/ttyS0
8 rild.libargs=-d /dev/ttyUSB3
9 ro.sf.lcd_density=240

다음에 linux atdt sk라고 검색하면 HSDPA를 PPP에서 동작 하도록 하는 내용이 있습니다.
이 부분은 x86에서 가져온 chat이 있다면 desktop과 동일하게 처리 되는 부분입니다.

아래 사이트에 가면 ppp에 관한 내용이 있고 HSDAP USB modem에 AT command로 connection하는 내용이 있습니다.


usb_modeswitch를 통해 usb modem을 storage card에서 modem으로 변경 해줍니다.
insmod /modules/usbserial.ko 'vendor=0x1d74 product=0x00c9'으로 ttyUSB 노드를 생성해 줍니다.
여기서 따옴표를 주지 않으면 두번째 parameter product인자가 제대로 넘어가지 않습니다.
ppp call gprs를 호출 하면 t-login 모뎀이 녹색으로 바뀌면서 동작합니다.

지금은 간략하게 작성하였고 차후에 추가 보강하겠습니다.

2010년 8월 1일 일요일

Ubuntu에서 T-Login(HSDPA) 사용하기...

Ubuntu에서 T-Login을 사용하여 보자.


Ubuntu는 10.04, kernel 2.6.32-24이며 T-Login은 LG Innotek에 LM-629HUL 모델입니다.

기존에 IM-H100 (C-Motech) 모델의 경우 USB로 연결하면 /dev/ttyACM0로 연결이되기 때문에 kppp나 gnome-ppp에서 modem설정에서 ttyACM0로 설정을 하면 동작 됩니다.

하지만 C-Motech의 CHU-628S나 LG Innotek의 LM-629HUL의 경우 usb-modeswitch를 통해 mode를 변경해 줘야 합니다.


usb-modeswitch 1.1.3


libusb-0.1.12

컴파일하여 설치 합니다.

usb를 연결 후 lsusb 실행하면 아래와 같이 17d4:1004가 생깁니다.

codewalker@codewalker-desktop:~$ lsusb
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 004: ID 0603:00f2 Novatek Microelectronics Corp.
Bus 004 Device 003: ID 045e:0040 Microsoft Corp. Wheel Mouse Optical
Bus 004 Device 002: ID 0ac8:301b Z-Star Microelectronics Corp. ZC0301 Webcam
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 003: ID 1d74:1004
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
codewalker@codewalker-desktop:~$

Window에서도 PID가 1004로 붙고 driver 설치 후 PID가 00c9으로 변경되는 것을 확인 할 수 있습니다. 아마도 linux에서도 정상 동작 시키려면 1004가 00c9이 되면 되겠죠.
그래서 usb-modeswitch가 필요한 것입니다.

하지만 usb-modeswitch-data-20100707.tar.bz2 여기에는 LM-629HUL 모델 script가 없습니다.

usb-modeswitch-data-20100707를 설치 하셨다면 /etc/usb_modeswitch.d/에 1d74:1004로 아래의 파일을 만듭니다.

########################################################
# LG Innotek LM-629HUL

DefaultVendor=0x1d74
DefaultProduct=0x1004

TargetClass=0xff

CheckSuccess=20

MessageContent="55534243b82e238c240000008000081c020000000000000000000000000000"

위에 값을 어떻게 찾을 것인가가 최대의 문제 였습니다.

C-Motech CHU-629S가 제일 비슷하다고 판단하였습니다.

16d8:700a를 복사 하였고 DefaultVendor와 DefaultProduct는 lsusb의 값이 1d74그리고 1004를 입력 한다.

TargetClass의 경우 필요한지 판단할 수 없지만 Windows에서 드라이버 등록 정보를 보면 Class가 0xff로 되어 있는 부분을 확인 할 수 있다.

그리고 MessageContent 이 값은 usb-modeswitch의 설명 처럼 SniffUSB를 이용하여 logging을 할 수 있지만 plug/unplug를 해도 엄청난 line의 log가 생깁니다.

일단 UsbSnoop.log을 봅시다.

어떤 값을 MessageContent에 넣어야 할지 판단이 되지 않는다.


위에 사이트에 보면 IRP_MN_SURPRISE_REMOVAL를 검색해 보면 될거란 힌트를 얻을 수 있습니다.

아래의 log는 연결하고자 하는 pc에서 얻는 값이다.

[2577207 ms] <<<>
-- URB_FUNCTION_SELECT_INTERFACE:
ConfigurationHandle = 0x892e6698
Interface: Length = 56
Interface: InterfaceNumber = 0
Interface: AlternateSetting = 0
Interface: Class = 0x00000008
Interface: SubClass = 0x00000006
Interface: Protocol = 0x00000050
Interface: InterfaceHandle = 8911d440
Interface: NumberOfPipes = 2
Interface: Pipes[0] : MaximumPacketSize = 0x00000200
Interface: Pipes[0] : EndpointAddress = 0x00000001
Interface: Pipes[0] : Interval = 0x00000000
Interface: Pipes[0] : PipeType = 0x00000002 (UsbdPipeTypeBulk)
Interface: Pipes[0] : PipeHandle = 0x8911d45c
Interface: Pipes[0] : MaxTransferSize = 0x00001000
Interface: Pipes[0] : PipeFlags = 0x00000000
Interface: Pipes[1] : MaximumPacketSize = 0x00000200
Interface: Pipes[1] : EndpointAddress = 0x00000081
Interface: Pipes[1] : Interval = 0x00000000
Interface: Pipes[1] : PipeType = 0x00000002 (UsbdPipeTypeBulk)
Interface: Pipes[1] : PipeHandle = 0x8911d47c
Interface: Pipes[1] : MaxTransferSize = 0x00001000
Interface: Pipes[1] : PipeFlags = 0x00000000
[2577207 ms] UsbSnoop - FilterDispatchAny(adb9cfd2) : IRP_MJ_INTERNAL_DEVICE_CONTROL
[2577207 ms] UsbSnoop - FdoHookDispatchInternalIoctl(adb9d1ea) : fdo=88fcbc08, Irp=8924c678, IRQL=0
[2577207 ms] >>> URB 6 going down >>>
-- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
PipeHandle = 8911d45c
TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK)
TransferBufferLength = 0000001f
TransferBuffer = b8513990
TransferBufferMDL = 00000000
00000000: 55 53 42 43 b8 2e 23 8c 24 00 00 00 80 00 08 1c
00000010: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00
UrbLink = 00000000
[2577209 ms] UsbSnoop - MyInternalIOCTLCompletion(adb9d126) : fido=00000000, Irp=8924c678, Context=8910a008, IRQL=2
[2577209 ms] <<<>
-- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
PipeHandle = 8911d45c
TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK)
TransferBufferLength = 0000001f
TransferBuffer = b8513990
TransferBufferMDL = 8a32bcc8
UrbLink = 00000000
[2577209 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_CAPABILITIES)
[2577209 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_CAPABILITIES)
[2577209 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_PNP_DEVICE_STATE)
[2577209 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_PNP_DEVICE_STATE)
[2577209 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577209 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577213 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577213 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577213 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577213 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_DEVICE_RELATIONS)
[2577213 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_SURPRISE_REMOVAL)
[2577213 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_SURPRISE_REMOVAL)
[2577243 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_REMOVE_DEVICE)
[2577818 ms] UsbSnoop - FilterAddDevice(adba1748) : DriverObject 890f2950, pdo 893516b8
[2577818 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_LEGACY_BUS_INFORMATION)
[2577818 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_LEGACY_BUS_INFORMATION)
[2577818 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_RESOURCE_REQUIREMENTS)
[2577818 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_QUERY_RESOURCE_REQUIREMENTS)
[2577818 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_FILTER_RESOURCE_REQUIREMENTS)
[2577818 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_FILTER_RESOURCE_REQUIREMENTS)
[2577818 ms] UsbSnoop - FilterDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_START_DEVICE)
[2577818 ms] UsbSnoop - FdoHookDispatchPnp(adba145c) : IRP_MJ_PNP (IRP_MN_START_DEVICE)
[2577819 ms] UsbSnoop - FilterDispatchAny(adb9cfd2) : IRP_MJ_SYSTEM_CONTROL
[2577822 ms] UsbSnoop - FilterDispatchAny(adb9cfd2) : IRP_MJ_INTERNAL_DEVICE_CONTROL
[2577822 ms] UsbSnoop - FdoHookDispatchInternalIoctl(adb9d1ea) : fdo=8a283890, Irp=890ce4d0, IRQL=0
[2577822 ms] >>> URB 1 going down >>>
-- URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:

#sudo usb_modeswitch -W -c /etc/usb_modeswitch.d/1d74:1004

codewalker@codewalker-desktop:~$ lsusb
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 004: ID 0603:00f2 Novatek Microelectronics Corp.
Bus 004 Device 003: ID 045e:0040 Microsoft Corp. Wheel Mouse Optical
Bus 004 Device 002: ID 0ac8:301b Z-Star Microelectronics Corp. ZC0301 Webcam
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 008: ID 1d74:00c9
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
codewalker@codewalker-desktop:~$

위와 같이 Product id가 바뀌어 있다.

이제 usbserial 드라이버를 올릴 차례입니다.

sudo lsmod usbserial vendor=0x1d74 product=0x00c9

그럼 /dev/ttyUSB 노드가 생성됩니다.

그후 kppp

오늘은 피곤해서 여기까지 낼 다시 작성하겠습니다.

2010년 6월 28일 월요일

android windows sdk 만들기...

Linux host에서 android SDK를 만들어 낼 수 있습니다.

eclipse에서 빌드가 되지 않는 몇몇 package를 빌드하기 위해 Linux host PC에서 SDK를 만들어 사용하려고 했지만 몇몇 문제로 그대로 사용 할 수 없는 문제가 있습니다.

간단하게 빌드된 SDK에서 android.jar와 layoutlib.jar 만 변경하여 빌드 환경을 구축 할 수는 있습니다.

위에 방법으로는 emulator까지 확인 할 수 없습니다.

그래서 Linux host PC에서 빌드한 SDK를 Windows버전으로 변경 하는 방법을 알아 보겠습니다.

소스 root에 development/docs/howto_build_SDK.txt란 문서가 있습니다.

이 문서를 따라서 한 것입니다.

먼저 cygwin을 설치하고 필요한 package들을 설치 합니다.
autoconf, bison, curl, flex, gcc, g++, git, gnupg, make,mingw-zlib, python, zip, unzip

linux에서 빌드한 sdk zip이 있으니 소스 받고 컴파일하는 부분은 생략 하겠습니다.

[git root]/development/build/tools/make_windows_sdk.sh [android sdk zip path] 경로
이렇게 실행 시켜 주시면 됩니다.

몇 가지 문제가 있는데 JDK를 보통 Program Files에 설치를 하는데 공백이 들어간 path는 에러가 납니다.

그래서 JDK 폴더를 공백이 없는 폴더에 복사 하시고 build/core/find-jdk/tools-jar.sh를 열어 편집 합니다.

2 echo $ANDROID_JAVA_HOME/lib/tools.jar
3 else
4 # JAVAC=$(which javac)
5 JAVAC=/cygdrive/d/project/jdk1.6.0_20/bin/javac
6 if [ -z "$JAVAC" ] ; then

위에 빨간 색과 같이 복사한 패스를 넣어 줍니다.

그리고 mgwz.dll을 /cygdrive/c/cygwin/bin에서만 찾습니다.
script에 고정 경로로 되어 있는 듯 합니다.
그래서 c:\cygwin\bin에 mgwz.dll을 하나 복사 해 둡니다.

완료가 되면 /tmp/android-sdk_[SDK_NUMBER]_windows폴더가 있고 경로에 zip이 생깁니다.

완료 후 기존 sdk 폴더에서 platform에 있는 것만 복사 하면 properties에서 여러 버전의 platform을 쉽게 바꿀 수 있습니다.

2010년 6월 24일 목요일

Launcher를 eclipse에서 빌드 하기

git에서 소스를 받으면 Launcher가 있습니다.
하지만 Eclipse에서 빌드가 되지 않습니다.

그래서 Eclipse에서 빌드하는 방법을 알아 보겠습니다.

android를 빌드 할 수 있는 Linux Host PC가 있어야 합니다.
환경 설정이 끝나면 빌드를 해 둡니다.

Eclipse에서 Launcher란 프로젝트를 생성하고 [git folder]\packages\apps\Launcher 폴더의 내용을 덮어 씁니다.

main.xml이 error가 납니다. main.xml을 삭제합니다.

이제 부터 eclipse상에서 빌드 에러 나는 부분을 하나씩 정리 하도록 하겠습니다.

수정 해야 할 path는 frameworks/base/core/java 폴더 입니다.

frameworks/base/core/java/android/app/Dialog.java:998:
frameworks/base/core/java/android/app/StatusBarManager.java:29:
frameworks/base/core/java/android/app/SearchManager.java:1282:
frameworks/base/core/java/android/view/View.java:1500:
frameworks/base/core/java/android/view/View.java:1552:
frameworks/base/core/java/android/view/View.java:1560:
frameworks/base/core/java/android/view/View.java:1568:
frameworks/base/core/java/android/view/View.java:1576:
frameworks/base/core/java/android/view/View.java:1585:
frameworks/base/core/java/android/view/View.java:1593:
frameworks/base/core/java/android/view/View.java:1696:
frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java:761:
frameworks/base/core/java/android/os/Vibrator.java:29:
frameworks/base/core/java/android/widget/CursorAdapter.java:53:
frameworks/base/core/java/android/content/Context.java:1240:
frameworks/base/core/java/android/content/DialogInterface.java:97:
frameworks/base/core/java/com/android/internal/util/XmlUtils.java:35:

위에 나열된 파일의 line을 보면 @hide가 있습니다. 이부분을 삭제하고 make sdk를 통해 sdk를 만듭니다.

완료가 되면 out/hos/linux-x86/sdk/android-sdk_[lang].[user]_linux-x86.zip이 생성 됩니다.

압축을 풀면 android.com에서 받은 sdk와 동일 구조의 폴더가 생깁니다.

linux에서 빌드된 sdk를 윈도우 버전으로 만드는 방법도 있습니다.

간단하게 기존 sdk에서 [android sdk root]/platforms/android-7/android.jar와 [android sdk root]/platforms/android-7/data/layoutlib.jar를 덮어씁니다.

layoutlib.jar에는 com.android.internal.util.XmlUtils와 같은 anroid.jar에 포함되지 않는 몇몇 class들이 들어 있습니다.

여기서 부터 제가 엄청 삽질 했던 부분입니다. 처음에는 Properties에서 Add JARs...를 통해 layoutlib.jar를 했었는데 아래 에러가 납니다.



Add JARs..가 아니고 Add Library...를 합니다.

아래 그림과 같이 Add Library에서 User Library를 하고 임의의 이름을 선택 후 layoutlib.jar를 선택 합니다.





그럼 몇몇 package에서 찾지 못하던 class들이 에러가 나지 않습니다.

eclipse에서 빌드는 되는데 설치 및 실행은 현재 상태로는 안됩니다. 런처를 설치 하기 위해서는 몇가지 단계가 있어 보입니다.

이 부분은 추후 수정 하도록 하겠습니다.

2010년 6월 9일 수요일

ListView.getCheckItemIds() 에러에 대해서...

안드로이드 2.1에서 ListView.getCheckItemIds()를 사용하면 return 값이 제대로 처리되어 넘어 오지 않습니다.


위에 링크를 따라 가봐도 bug report가 되어 있는 상황이다.

그래서 다른 방법으로 check된 id를 구해 오는 방법입니다.

public class TestActivity extends ListActivity {
{
private ListView mList;

protected void onCreate(Bundle savedInstanceState) {
...
mList = getListView();
...
}

protected void onPause() {
super.onPause();
List list = new ArrayList();
SparseBooleanArray a = mList.getCheckedItemPositions();
for(int i = 0; i <>
if (a.valueAt(i)) {
list.add(mList.getAdapter().getItemId((a.keyAt(i)));
}
}
}

getCheckItemIds()가 대신 getCheckedItemPositions()으로 position들을 구해서 apater에서 ids를 구해 오도록 하였다.

2010년 5월 26일 수요일

다중 모니터에서 단축키로 창 옮기기


그래픽 카드가 nVidia의 경우 nView Desktop란 프로그램에서 창을 단축키를 지정하여 창을 mouse dragging 없이 이동이 가능 하였습니다.

하지만 이번에 바꾼 노트북에는 ATI라서 이 기능을 사용하지 못하여 답답하였습니다.

듀얼 모니터 이상을 사용하시는 분들 중에 nVidia의 nView Desktop의 단축키에서 화면 이동을 사용해 보세요. 무척 편리합니다.

그래서 찾던 중 nView Desktop의 그 기능 그대로 단축 키로 창 옮기기만 구현된 프로그램이 있어서 소개 드립니다.

사실 nView의 창 옮기기만 필요한데 nView Desktop 활성화해야 합니다.

몇 년을 써본 결과 좀 무겁고 windows가 상황이 안 좋아지면 더 영향을 받았는데 이 프로그램은 nView Desktop 전체 기능을 사용하지 않고도 제가 원하는 창을 다른 모니터로 옮기기가 단축키로 가능합니다.



전 단축 키를 Ctrl + 1 으로 지정하여 사용 합니다.

2010년 5월 4일 화요일

해피빈 기부하기


"서울시각장애인복지관"콩기부 부탁드립니다.

메일 작성시 클릭한번! 정말 쉬운 콩기부!


꼭!!! 클릭한 후 메일을 보내시면 메일 한번 보내면 한 개 적립!

한 달 동안 내 아이디에 콩적립 10개 가능.

적립된 콩은 네이버메인화면 우측 상단에 해피빈 클릭한 후 기부가능


해피빈 검색창에 서울시각장애인복지관 입력 후 검색 클릭



클릭 하시고 들어오신 후 기부해주세요.




보유한 콩을 보시면 현재보유 콩이 나오구요.

가지고 계신 콩을 모두기부하셔도 되고 원하시는 만큼 기부하셔도 됩니다.


[android]odroid에 Gallay 3D 올리기...

odroid 2.1 버전에 Gallary 3D이 포함되어 있지 않습니다.

odroid에 Gallary 3D를 올려 보겠습니다.

git로부터 다운 받은 소스 트리에서 package\apps\Gallary3D란 폴더가 있습니다.

Eclipse에서 Gallary3D를 Create project from existing source로 불러 오면 overlaps the location of another project 에러 메세지가 나오면서 안됩니다.

import로 불러 오려니 .project나 .classpath가 없어서 그것 역시 안됩니다.

그래서 일단 Gallary로 new project 하고 거기에 git에 받은 폴더를 덮어 쓰시면 됩니다.



packages\apps\에 Launcher 소스도 있습니다. 오늘부터 이 소소를 분석 해봐야 겠네요.

2010년 5월 3일 월요일

[android] Home example 배경 화면 유지 하기

example 중에 Home example을 보면 Launcher의 대략적인 구조를 알 수 있습니다.
그래서 Home을 보고 이것 저것 바꿔 가면서 공부 중인데 화면이 회전 되면 배경이 지원지는 문제가 발생 합니다. 그래서 portrait에서 landscape 전화 시 배경화면이 유지도록 수정 해 보겠습니다.


portrait에서 landscape 전화 시 배경이 지워짐.


기본 개념은 wallpaper을 얻어와 서 자신의 window의 background로 설정 하는 것입니다.

private void setDefaultWallpaper() {
if (!mWallpaperChecked) {
Drawable wallpaper = peekWallpaper();
if (wallpaper == null) {
try {
clearWallpaper();
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to clear wallpaper " + e);
}
} else {
getWindow().setBackgroundDrawable(new ClippedDrawable(wallpaper));
}
mWallpaperChecked = true;
}
}

이유는 모르겠지만 화면이 회전이 되면 다시 setBackgroundDrawable()을 해줘야 합니다.

화면 회전 시 Activity의 lifecycle의 onPause() -> onResume이 발생합니다.

그래서 onResume() 함수를 override 후 아래와 같이 setBackgroundDrawable()을 해 주면 됩니다.

@Override
protected void onResume() {
super.onResume();
getWindow().setBackgroundDrawable(new ClippedDrawable(getWallpaper()));
bindRecents();
}



아주 간단하게 수정이 되었지만 이 부분을 찾는 데 많이 삽질 했습니다.
덕분에 Activity, Window, View의 큰 그림을 이해하게 되었네요.

2010년 5월 1일 토요일

[android] Eclipse에서 android 및 java source code browsing하기기

위와 관련해서 인터넷을 검색해 보면 git로 소스를 받고 frameworks/base/java를 sdk를 받은 폴더에 sources를 만들고 java밑에 있는 파일을 sources에 복사 하면 된다고 나와 있습니다.
이전에 1.6버전일 때는 저 역시 이렇게 하면 됐던걸로 기억하고 있습니다.
하지만 2.1 sdk를 받고 eclipse에서 모든 update를 한 후에는 위에 방법으로 보여 지지 않습니다.
최신 버전으로 업데이트를 받고 나면 platform 폴더에 버전 별로 폴더가 생깁니다.



android-7 폴더에 sources를 만들고 거기에 git에서 받은 frameworks/base/core/java/android와 frameworks/base/java/com을 복사 하시면 됩니다.

그리고 반드시 환경 변수PATH에 android sdk 경로를 추가 해 주셔야 합니다.

그리고 이제 java 소스를 볼 수 있는 방법 입니다.
java를 설치 하고 나면 일반적으로 C:\Program Files\Java\jdk1.6.0_20\src.zip 파일 있습니다.
압축을 풀고 src/java폴더를 [android sdk]\platforms\android-7\sources에 복사 하여 주시면 됩니다.

위에 그림 처럼 android/com/java가 존재 하면 됩니다.

그외에 graphics와 같은 폴더도 동일하게 복사 해서 넣어 주시면 됩니다.


2010년 4월 30일 금요일

[android] 방향 알아 내기...

Android 단말기에서 Landscape, Portrait 상태 인지 확인 하는 방법 입니다.

public TestView extends View {

public TestView(Context)
{
super(context);
OrientationEventListener oel = new OrientationEventListener(getContext()) {
@Override
public void onOrientationChanged(int orientation) {
if (orientation >= 90) {
Toast.makeText(getContext(), "LandScape", Toast.LENGTH_SHORT).show();
} else if (orientation == 0) {
Toast.makeText(getContext(), "Portrait", Toast.LENGTH_SHORT).show();
}
}
}
oel.enable();

...
}

orientation이 단말기의 angle입니다.

0이면 portrait이고 90이면 landscape입니다.

여기서 특히한 점은 Accelerometter와 구현 방법이 차이가 납니다.
SensorManager에 listener를 register하는 방식이 아닙니다.

아래 소스틑 snake에서 Aceelerometer로 구현 한 것 입니다.

SensorManager sensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
Sensor mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
SensorEventListener sel = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;

if (java.lang.Math.abs(values[0]) - java.lang.Math.abs(values[1]) > 0) {
if (values[0] <>
if (mDirection != WEST) {
mNextDirection = EAST;
}
} else if (values[0] >= 0) {
if (mDirection != EAST) {
mNextDirection = WEST;
}
}
} else {
if (values[1] <>
if (mDirection != SOUTH) {
mNextDirection = NORTH;
}
} else if (values[1] >= 0) {
if (mDirection != NORTH) {
mNextDirection = SOUTH;
}
}
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
};
sensorManager.registerListener(sel, mSensor,SensorManager.SENSOR_DELAY_GAME);
}

SensorManager 부분이 필요 없습니다.

Eclipse에 java Syntax Color 변경하기

Eclipse는 Syntax Color 밋밋합니다.
Syntax Color를 변경하는 방법을 찾아서 posting합니다.





vibrantink theme를 적용한 모습니다.

여기에 vrapper plugin 도 설치하여 사용 중인데 조금 vi 스러워 졌네요.

2010년 4월 27일 화요일

[android]Snake 게임을 Accelerometer로 하기

android 예제 중에 Snake 게임이 있습니다.




















방향 키로 snake를 욺직일 수 있는데 이번에는 Accelerometer 센서로 snake를 조종할 수 있도록 수정 해 보겠습니다.

protected void initAccelator() {
SensorManager sensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
Sensor mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
SensorEventListener sel = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;

if (java.lang.Math.abs(values[0]) - java.lang.Math.abs(values[1]) > 0) {
if (values[0] <>
if (mDirection != WEST) {
mNextDirection = EAST;
}
} else if (values[0] >= 0) {
if (mDirection != EAST) {
mNextDirection = WEST;
}
}
} else {
if (values[1] <>
if (mDirection != SOUTH) {
mNextDirection = NORTH;
}
} else if (values[1] >= 0) {
if (mDirection != NORTH) {
mNextDirection = SOUTH;
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
};
sensorManager.registerListener(sel, mSensor, SensorManager.SENSOR_DELAY_GAME);
}

위에 함수를 SnakeView에 추가 후 생성자에서 호출합니다.

event.values에서 values[0]이 x축 이고 [1]이 y축 [2]가 z축입니다.
x축과 y 축이 동시에 계속 발생 하기 때문에 좌,우,위,아래 중 하나를 선택하기 위해 x축과 y축 중에 값이 더 큰 쭉으로 선택하도록 하고 축의 방향은 음수인지 양수인지 판단 하시면 됩니다.

2010년 4월 23일 금요일

Notepadv2

http://developer.android.com/resources/tutorials/notepad/notepad-ex3.html

Notepadv2 예제에서 NoteEdit 화면에서 back 버튼을 누르면 죽습니다.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode != RESULT_OK) {
return;
}

Bundle extras = intent.getExtras();
...
}

위에 한줄 추가 해주면 죽지 않습니다.
Notepadv2에서 Intent로 NoteEdit를 호출 후 back 시 onActivityResult()가 호출 되고 NoteEdit가 back button으로 finish 되면 보낸 Intent가 없기 때문에 죽습니다.