ラベル 한글 の投稿を表示しています。 すべての投稿を表示
ラベル 한글 の投稿を表示しています。 すべての投稿を表示

2011年11月14日月曜日

【お前の体重を見ろ!】バージョン2のアイコン

久しぶりのブログだな。
今日は【お前の体重を見ろ!】をバージョンアップしようと思ってアイコンを作ってみた。
今回はiPhoneとAndroid両方対応するつもりで、
アンドロイドっぽくないアイコンを考えて、俺が唯一に作れるクマを選んでデブにしてみた。
バージョン1とキャラクターが違うけど、まぁ。。いい。

これが頑張って作った太ったクマ!
俺ちょっとデザインのうでが上がったんじゃない?
ちなみに2年前の俺はこんなデザインをしていた。
おお!発展してる。

2010年11月28日日曜日

갤럭시탭 에뮬레이터를 설치해보자.

갤럭시탭 에뮬레이터를 설치해보자.

참고 사이트는 「http://innovator.samsungmobile.com/galaxyTab.do

Android SDK and AVD Manager를 열고,


Available Packages에
「http://innovator.samsungmobile.com/android/repository/srepository.xml」사이트를 추가.


추가후 「GALAXY Tab Addon」을 설치.




애뮬레이터 설치.




실행해보니.
제 컴퓨터에서는 한화면에 안보이는군요.
iPad 애뮬레이터처럼 50%로 축소해서 볼 수 있으면 좋을 듯 하지만 좀 아쉽군요.

2010年9月3日金曜日

[Android] 니 몸무게를 봐라! v1.0

「니 몸무게를 봐라!」v1.0를 릴리스하였다.
「니 몸무게를 봐라!」는 간단한 체중관리 무료 안드로이드 어플이다.

Support languages
- English
- 日本語
- 한국어

v1.0의 변경점은 (전 버젼 v9.0 링크)
1. 공유기능추가
- 메일 또는 트위터 클라이언트와 연동
(용감하게 자신의 몸무게 그래프를 공개가능 ^^;)
2. CSV가져오기 기능 추가.
3. 아이콘 변경

다음번에는 사용자들 요청도 있고해서 잠금기능을 추가할까 생각중이다.







2010年9月1日水曜日

[Android] 로그 레벨 설정방법

Android 로그 레벨 설정방법

에뮬레이터와 단말기의 로그레벨이 아무래도 기본 「INFO」인듯하다.

테스트해보면


Log.isLoggable(TAG, Log.VERBOSE) == false
Log.isLoggable(TAG, Log.DEBUG) == false
Log.isLoggable(TAG, Log.INFO) == true
Log.isLoggable(TAG, Log.WARN) == true
Log.isLoggable(TAG, Log.ERROR) == true



그렇기 때문에 아래와 같은 경우는 로그가 출력되지만,

Log.d(TAG, msg); // 로그레벨과 상관없이 출력됨.



다음의 코드는 출력되지 않는다.

if(Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, msg); // 로그레벨이 「DEBUG」이상인 경우, 출력됨.
}


특정 로그태그의 로그레벨을 설정하는 방법은

$ ./adb shell setprop log.tag.<your_log_tag> <level>



예를 들어 TAG가 「BABUKUMA」인 경우는

$ ./adb shell stop
$ ./adb shell setprop log.tag.BABUKUMA DEBUG
$ ./adb shell start


다시 테스트해본 결과는

Log.isLoggable(TAG, Log.VERBOSE) == false
Log.isLoggable(TAG, Log.DEBUG) == true
Log.isLoggable(TAG, Log.INFO) == true
Log.isLoggable(TAG, Log.WARN) == true
Log.isLoggable(TAG, Log.ERROR) == true

2010年8月30日月曜日

[iPhone] UIWebView의 링크를Safari로 열기

UIWebView의 링크를 Safari로 열기

참고링크:

결론은 UIWebViewDelegate를 이용한다.

Interface Builder에서 UIWebView의 delegate를 설정하고,


다음의 코드를 추가한다.
// 링크를 클릭했을 때, Safari를 열기위한 처리.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSLog(@"URL = %@", [[request URL] absoluteString]);
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}

return YES;
}


테스트시 사용한 전체 코드
WebViewTestAppDelegate.h
//
// WebViewTestAppDelegate.h
// WebViewTest
//
// Created by BABUKUMA on 10/08/30.
// Copyright babukuma.com 2010. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface WebViewTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
IBOutlet UIWebView *webView;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end


WebViewTestAppDelegate.m
//
// WebViewTestAppDelegate.m
// WebViewTest
//
// Created by BABUKUMA on 10/08/30.
// Copyright babukuma.com 2010. All rights reserved.
//

#import "WebViewTestAppDelegate.h"

@implementation WebViewTestAppDelegate

@synthesize window;


#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

[window makeKeyAndVisible];

// WebViewの初期表示
NSLog(@"BABUKUMA : WebViewの初期表示");
NSURL *url = [NSURL URLWithString:@"http://babukuma.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest: request];

return YES;
}

// リンクをクリック時、Safariを起動する為の処理
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSLog(@"URL = %@", [[request URL] absoluteString]);
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}

return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
}


- (void)applicationWillTerminate:(UIApplication *)application {
}


#pragma mark -
#pragma mark Memory management

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}


- (void)dealloc {
[window release];
[super dealloc];
}


@end

2010年8月5日木曜日

[Android] 니 몸무게를 봐라! v0.9

오늘 「니 몸무게를 봐라!」v0.9를 업데이트하였다.
「니 몸무게를 봐라!」는 간단한 체중관리 무료 안드로이드 어플이다.

v0.9
1. 체지방그래프 추가
2. BMI와 BMR 용어설명 추가
3. 버그수정
- 체중그래프에 (kg)만 표시되는 버그







다음에는 CSV파일 가져오기를 추가해야겠다.

2010年7月7日水曜日

Scala WebApplication + Maven 프로젝트 구성

Scala WebApplication + Maven 프로젝트 구성

1. WebApplication프로젝트 작성

mvn archetype:create -DgroupId=com.babukuma -DartifactId=Scala.Web -DarchetypeArtifactId=maven-archetype-webapp


2. 디렉토리 구성
 - java소스
  ・src/main/java
  ・src/test/java (테스트용)
 - scala소스
  ・src/main/scala
  ・src/test/scala (테스트용)
 - resources
  ・src/main/resources
  ・src/test/resources (테스트용)
 - WebApplication파일
  ・src/main/webapp
  ・src/test/webapp (테스트용)



3. pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.babukuma</groupId>
<artifactId>Scala.Web</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Scala.Web Maven Webapp</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdkVersion>1.6</jdkVersion>
<junitVersion>4.8.1</junitVersion>
<scalaVersion>2.8.0.RC7</scalaVersion>
<scalaPluginVersion>2.12</scalaPluginVersion>
</properties>

<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junitVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-servlet_2.5_spec</artifactId>
<version>1.1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scalaVersion}</version>
</dependency>
</dependencies>
<build>
<finalName>Scala.Web</finalName>

<!-- To define the plugin version in your parent POM -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
</plugin>
</plugins>
</pluginManagement>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${jdkVersion}</source>
<target>${jdkVersion}</target>
</configuration>
</plugin>

<!-- To use the plugin goals in your POM or parent POM -->
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmArgs>
<jvmArg>-Xms64m</jvmArg>
<jvmArg>-Xmx1024m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>

<!-- To use the report goals in your POM or parent POM -->
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
</plugin>
</plugins>
</reporting>
</project>



4. 테스트용 소스(Servlet)
HelloScalaServlet.scala

package com.babukuma

import javax.servlet.http._

class HelloScalaServlet extends HttpServlet {
override def doGet(request : HttpServletRequest, response : HttpServletResponse) =
response.getWriter().print("Hello Scala")
}


5. web.xml

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Scala Web Application</display-name>
<servlet>
<servlet-name>helloScala</servlet-name>
<servlet-class>com.babukuma.HelloScalaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloScala</servlet-name>
<url-pattern>/helloScala</url-pattern>
</servlet-mapping>
</web-app>


6. 동작확인

2010年7月6日火曜日

Scala + Maven2 프로젝트 구성

참조URL
- http://scala-tools.org/mvnsites/maven-scala-plugin/
- http://www.scala-lang.org/node/345

pom.xml파일

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.babukuma</groupId>
<artifactId>scala.test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>scala.test</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junitVersion>4.8.1</junitVersion>
<scalaVersion>2.8.0.RC7</scalaVersion>
<scalaPluginVersion>2.12</scalaPluginVersion>
</properties>

<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junitVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scalaVersion}</version>
</dependency>
</dependencies>

<build>
<!--
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
-->
<!-- To define the plugin version in your parent POM -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- To use the plugin goals in your POM or parent POM -->
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmArgs>
<jvmArg>-Xms64m</jvmArg>
<jvmArg>-Xmx1024m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>

<!-- To use the report goals in your POM or parent POM -->
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${scalaPluginVersion}</version>
</plugin>
</plugins>
</reporting>
</project>


디렉토리 구성
- java소스
・src/main/java
・src/test/java (테스트용)
- scala소스
・src/main/scala
・src/test/scala (테스트용)
- resources
・src/main/resources
・src/test/resources (테스트용)



테스트 소스
HelloScala.scala

package com.babukuma.scala

object HelloScala {

def main(args: Array[String]): Unit = {
println("Hello Scala!")

val helloJava = new HelloJava
helloJava.printHelloJava()
}
}


HelloJava.java

package com.babukuma.scala;

public class HelloJava {
public void printHelloJava() {
System.out.println("Hello Java!");
}
}


컴파일과 패키징

$ mvn compile
$ mvn package


실행



$ mvn scala:run -DmainClass=com.babukuma.scala.HelloScala
Hello Scala!
Hello Java!

2010年4月9日金曜日

SQLite과 H2의 성능비교

궁금해서 임베디드 DB인 H2와 SQLite3를 비교해보았다.

SQLite 버젼 : 3.6.23
H2 버젼 : 1.2.132

둘다 사용할 프로젝트가 Java인 관계로 언어는 Java로.
SQLite는 JDBC드라이버인 SQLiteJDBC를 사용하였다.

SQLiteJDBC : http://www.zentus.com/sqlitejdbc/

테스트 환경 1


- CPU : 2.53 GHz Intel Core 2 Duo
- Memory : 4GB
- OS : Mac OS X 10.6.3
구분SQLiteH2 EmbeddedH2 In-memory
Insert (100만건)0.5 초0.9 초0.9 초
Select (100만건)0.002 초0.015 초0.07 초
File size2,048 bytes14,336 bytes-


테스트 환경 2


- CPU : 2.53 GHz Intel Core 2 Duo
- Memory : 4GB
- OS : Mac OS X 10.6.3
구분SQLiteH2 EmbeddedH2 In-memory
Insert (100만건)0.5 초1.3 초1.5 초
Select (100만건)0.004 초0.09 초0.095 초
File size2,048 bytes14,336 bytes-


비록 간단한 테스트이지만 대략적으로 SQLite가 우세한 듯 보인다.
특히 성능이 낮은 머신에서 H2와의 성능차이가 뚜렷하다.
그리고 H2는 여지껏 「In-memory」방식이 「임베디드」방식보다 성능이 더 좋을 줄 알았는데,
전혀 생각과 틀려 충격이었다.


테스트용 소스는 아래와 같다.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

public class SqlitePerformance {
private static final int ROW_COUNT = 1000000;
private static final int MAX = 100;

/**
* SQLite3 Embedded
*
* @author babukuma
* @since 2010/04/09
* @param args
*/
public static void main(String[] args) throws Exception {
System.out.println("SQLite TEST [" + ROW_COUNT + "件]");
Class.forName("org.sqlite.JDBC");
Connection conn = DriverManager
.getConnection("jdbc:sqlite:babukuma.sqllite.db");

Statement stat = conn.createStatement();
stat.executeUpdate("drop table if exists babukuma;");
stat
.executeUpdate("create table babukuma (id integer primary key, value integer);");

conn.setAutoCommit(false);
System.out.print("Insert Test : ");
long startTime = System.currentTimeMillis();

PreparedStatement prep = conn
.prepareStatement("insert into babukuma values (?, ?);");

for (int i = 0; i < ROW_COUNT; i++) {
prep.setInt(1, i);
prep.setInt(2, i);
prep.addBatch();

if (i > 0 && (i % MAX) == 0 && i == ROW_COUNT - 1) {
prep.executeBatch();
prep.clearBatch();
}
}

conn.commit();

long endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

System.out.print("Select Test : ");
startTime = System.currentTimeMillis();
ResultSet rs = stat.executeQuery("select * from babukuma;");
while (rs.next()) {
rs.getInt("id");
rs.getInt("value");
// System.out.println("id = " + rs.getInt("id"));
// System.out.println("value = " + rs.getString("value"));
}
rs.close();
endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

conn.close();
}
}


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.h2.jdbcx.JdbcConnectionPool;

public class H2Performance {
private static final int ROW_COUNT = 1000000;
private static final int MAX = 100;

/**
* H2 Embedded
*
* @author babukuma
* @since 2010/04/09
* @param args
*/
public static void main(String[] args) throws Exception {
System.out.println("H2 TEST [" + ROW_COUNT + "件]");
JdbcConnectionPool cp = JdbcConnectionPool.create(
"jdbc:h2:babukuma.h2", "", "");
Connection conn = cp.getConnection();

Statement stat = conn.createStatement();
stat.executeUpdate("drop table if exists babukuma;");
stat
.executeUpdate("create table babukuma (id int primary key, value int);");

conn.setAutoCommit(false);
System.out.print("Insert Test : ");
long startTime = System.currentTimeMillis();

PreparedStatement prep = conn
.prepareStatement("insert into babukuma values (?, ?);");

for (int i = 0; i < ROW_COUNT; i++) {
prep.setInt(1, i);
prep.setInt(2, i);
prep.addBatch();

if (i > 0 && (i % MAX) == 0 && i == ROW_COUNT - 1) {
prep.executeBatch();
prep.clearBatch();
}
}

conn.commit();

long endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

System.out.print("Select Test : ");
startTime = System.currentTimeMillis();
ResultSet rs = stat.executeQuery("select * from babukuma;");
while (rs.next()) {
rs.getInt("id");
rs.getInt("value");
// System.out.println("id = " + rs.getInt("id"));
// System.out.println("value = " + rs.getString("value"));
}
rs.close();
endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

conn.close();
}
}


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.h2.jdbcx.JdbcConnectionPool;

public class H2InMemoryPerformance {
private static final int ROW_COUNT = 1000000;
private static final int MAX = 100;

/**
* H2 In memory
*
* @author babukuma
* @since 2010/04/09
* @param args
*/
public static void main(String[] args) throws Exception {
System.out.println("H2 In Memory TEST [" + ROW_COUNT + "件]");
JdbcConnectionPool cp = JdbcConnectionPool
.create("jdbc:h2:mem:", "", "");
Connection conn = cp.getConnection();

Statement stat = conn.createStatement();
stat.executeUpdate("drop table if exists babukuma;");
stat
.executeUpdate("create table babukuma (id int primary key, value int);");

conn.setAutoCommit(false);
System.out.print("Insert Test : ");
long startTime = System.currentTimeMillis();

PreparedStatement prep = conn
.prepareStatement("insert into babukuma values (?, ?);");

for (int i = 0; i < ROW_COUNT; i++) {
prep.setInt(1, i);
prep.setInt(2, i);
prep.addBatch();

if (i > 0 && (i % MAX) == 0 && i == ROW_COUNT - 1) {
prep.executeBatch();
prep.clearBatch();
}
}

conn.commit();

long endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

System.out.print("Select Test : ");
startTime = System.currentTimeMillis();
ResultSet rs = stat.executeQuery("select * from babukuma;");
while (rs.next()) {
rs.getInt("id");
rs.getInt("value");
// System.out.println("id = " + rs.getInt("id"));
// System.out.println("value = " + rs.getString("value"));
}
rs.close();
endTime = System.currentTimeMillis();
System.out.println(((endTime - startTime) / 1000.) + "秒");

conn.close();
}
}

2010年3月15日月曜日

[SWT] SWT on 64bit Mac OS X

Mac OS X 10.6.2 Snow Leopard위에
SWT어플리케이션을 실행시키면 뭔가 제대로 실행이 안되는 현상이 있다.
그래서 이것저것 찾아보니 몇가지 옵션이 필요한 듯 하다.

「-d64」옵션과 「-XstartOnFirstThread」옵션

「-d64」옵션 : 명확하게 64bit VM에서 동작하도록 지정해준다.

「-XstartOnFirstThread」옵션 : Mac에서 SWT어플리케이션이 정상적으로 동작하는데 필요한 옵션인 듯 하다. SWT에 한정된 옵션은 아닌 듯.

두 옵션을 실제 사용해 보면
java -d64 -XstartOnFirstThread -jar babukuma_swt_test.jar

실제 테스트에서 문제없이 작동하였다.

참고한 사이트.
http://www.eclipse.org/swt/macosx
http://www.eclipse.org/swt/faq.php

2010年3月10日水曜日

[Android] Android NDK r3를 사용해보다.

이번에 새로 릴리즈된 Android NDK r3를 사용해보았다.

OS는 Mac OS X 10.6.2

android-ndk-r3-darwin-x86.zip파일을 「http://developer.android.com/」사이트에서 다운로드하고 적당한 곳에 압축을 풀어놨다.
나는 「/Users/babukuma/dev/android-ndk-r3」이 경로에 풀어놨다.(이하 $NDK)

1. Android프로젝트 생성.
테스트용으로 프로젝트를 생성.

생성되면 프로젝트는 아마도 이런 구조.

여기서 「jni」폴더를 생성.


2. 「Application.mk」파일을 작성.
프로젝트 밑에 「Application.mk」파일을 작성한다.

여기서는 모듈명을 「jnitest」로 하였다.
작성법은 「$NDK/docs/APPLICATION-MK.TXT」파일을 참조.

# Application.mk
APP_PROJECT_PATH := $(call my-dir)
APP_MODULES := jnitest


3. Native메소드를 작성
「Main.java」파일에 작성하였다.

package com.babukuma.android.test.jni;

import android.app.Activity;
import android.os.Bundle;

public class Main extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

public native String helloJNI();

public native int add(int a, int b);
}


4. 헤더파일 생성.
프로젝트 디렉토리에서 「javah」를 커맨드로 헤더파일「jni/jnitest.h」을 생성.

생성된 「jnitest.h」파일

/* DO NOT EDIT THIS FILE - it is machine generated */
#include<jni.h>
/* Header for class com_babukuma_android_test_jni_Main */

#ifndef _Included_com_babukuma_android_test_jni_Main
#define _Included_com_babukuma_android_test_jni_Main
#ifdef __cplusplus
extern"C" {
#endif
/*
* Class: com_babukuma_android_test_jni_Main
* Method: helloJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_babukuma_android_test_jni_Main_helloJNI
(JNIEnv *, jobject);

/*
* Class: com_babukuma_android_test_jni_Main
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_babukuma_android_test_jni_Main_add
(JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


5. C소스 작성
「jni」디렉토리에 「jnitest.c」파일을 작성한다.

작성한 「jnitest.c」파일

#include<jni.h>
#include"jnitest.h"

JNIEXPORT jstring JNICALL Java_com_babukuma_android_test_jni_Main_helloJNI(
JNIEnv *env, jobject thisObj) {
jstring result = (*env)->NewStringUTF(env, "Hello JNI !");
return result;
}

JNIEXPORT jint JNICALL Java_com_babukuma_android_test_jni_Main_add(JNIEnv *env,
jobject thisObj, jint a, jint b) {
jint result = a + b;
return result;
}


6. 「Android.mk」파일을 작성.
「jni」디렉토리에 「Android.mk」파일을 작성한다.
작성법은 「$NDK/docs/ANDROID-MK.TXT」을 참조.

작성한「Android.mk」파일

# Android.mkLOCAL_PATH := $(call my-dir)

include$(CLEAR_VARS)

LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := jnitest.c

include$(BUILD_SHARED_LIBRARY)


7. C소스 빌드
빌드를 하기위해서는 프로젝트의 PATH가 「$NDK/apps/<프로젝트명>」여야할 필요가 있으므로 링크를 작성하였다.

$NDK디렉토리에서 「make APP=jnitest」로 빌드.
여기서 「jnitest」는 「$NDK/apps/」밑의 프로젝트폴더명이 된다.

Eclipse를 새로고침하면 「libs/armeabi/libjnitest.so」파일이 생성되어있는 것을 알 수 있다.


8. Java소스를 수정하여 실행해봄.
수정후의 「Main.java」파일

package com.babukuma.android.test.jni;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class Main extends Activity {
static {
System.loadLibrary("jnitest");
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// call helloJNI()
TextView result1 = (TextView) findViewById(R.id.text_result_1);
result1.setText(helloJNI());

// call add(1, 2)
TextView result2 = (TextView) findViewById(R.id.text_result_2);
result2.setText("1 + 2 = " + add(1, 2));
}

public native String helloJNI();

public native int add(int a, int b);
}


수정후 실행해보면 Native메소드가 문제없이 호출된 것을 알 수 있다.

2010年2月22日月曜日

[Android] 가속도센서를 사용해보다.

Android 가속센서를 사용해보다.

가속도센서를 등록하면 SensorEvent의 values배열에 x, y, z의 가속도가 순서대로 들어오기 때문에 의외로 간단하게 사용할 수 있었다.

가속도센서에 따라 이미지가 움직인다.



Activity를 등록할 때 화면을 수직으로 고정시켰다.(수직방향이 계산하기 쉬워서)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.babukuma.android.test" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SensorActivity" android:screenOrientation="portrait"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="4" />
</manifest>


확인하기 편하도록 화면에는 간단한 이미지를 출력하고 가속도에 따라 위치를 변경시키도록 하였다.

package com.babukuma.android.test;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class SensorActivity extends Activity implements SensorEventListener {
private static final String LOG_TAG = "BABUKUMA";

private SensorManager sensorManager;
private SensorView sensorView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sensorView = new SensorView(this);
setContentView(sensorView);

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

// 加速度センサー
List<Sensor> sensors = sensorManager
.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensors.size() > 0) {
sensorManager.registerListener(this, sensors.get(0),
SensorManager.SENSOR_DELAY_GAME);
}
}

@Override
protected void onDestroy() {
super.onDestroy();
sensorManager.unregisterListener(this);
}

@Override
public void onAccuracyChanged(final Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}

@Override
public void onSensorChanged(final SensorEvent event) {
Sensor sensor = event.sensor;
switch (sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
// Log.d(LOG_TAG, "ACCELEROMETER : " + event.values[0] + ","
// + event.values[1] + "," + event.values[2]);
sensorView.move(event.values[0], event.values[1]);

break;
default:
break;
}
}

class SensorView extends View {
private static final int KUMA_SIZE = 50;
private Bitmap kuma;
private int w;
private int h;
private float x;
private float y;

public SensorView(Context context) {
super(context);
requestWindowFeature(Window.FEATURE_NO_TITLE);
kuma = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_kuma_50);
}

/**
* クマを移動する。
*
* @param mx
* @param my
*/
public void move(float mx, float my) {
this.x -= (mx * 4f);
this.y += (my * 4f);

if (this.x < 0) {
this.x = 0;
} else if ((this.x + KUMA_SIZE) > this.w) {
this.x = this.w - KUMA_SIZE;
}

if (this.y < 0) {
this.y = 0;
} else if ((this.y + KUMA_SIZE) > this.h) {
this.y = this.h - KUMA_SIZE;
}

invalidate();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
this.w = w;
this.h = h;
this.x = (w - KUMA_SIZE) / 2f;
this.y = (h - KUMA_SIZE) / 2f;
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(kuma, x, y, null);
}
}
}

2010年2月5日金曜日

Mac OS X에 MySQL 설치

환경
OS : Mac OS X 10.6.2
MySQL : 5.1.43

1. MySQL 다운로드
  - http://dev.mysql.com/downloads/mysql/ 에서 다운로드
  - mysql-5.1.43-osx10.6-x86_64.dmg

2. 인스톨
  - 다운로드한 파일을 실행


  - 「mysql-XXXXXX.pkg」(여기서는 「mysql-5.1.43-osx10.6-x86_64.pkg」)을 실행

  - MySQL.prefPane을 설치(더블클릭으로.)



3. MySQL서버 시작과 정지
  - 콘솔창에서 커맨드로도 가능하지만 시스템환경설정화면에서 간단하게 조작가능.



4. MySQL언인스톨
  - MySQL.prefPane제거


  - mysql이 설치된 디렉토리를 삭제
    디폴트면 「/usr/local/mysql-5.1.43-osx10.6-x86_64」와 「/usr/local/mysql」를 삭제하면 된다.

2010年2月1日月曜日

[Django] Django를 Tomcat에서 실행하기.

Jython을 사용하여 Django프레임워크를 Tomcat에서 실행하여 보았다.

1. Jython을 설치
  - http://jython.org 에서 다운로드
  - 2.5.1버젼을 인스톨하였다.
  - 환경변수에 「JYTHON_HOME」작성
  - [Jython] MACでjythonをインストールしてみた。를 참고.

2. Django를 설치
  - http://www.djangoproject.com
  - Django-1.1.1.tar.gz을 다운로드하였다.
  - # sudo jython setup.py install 로 설치

3. django-jython을 설치
  - http://code.google.com/p/django-jython
  - # sudo jython setup.py install

4. Django용 프로젝트를 작성(여기서는 django_test로)
  - # jython $JYTHON_HOME/bin/django-admin.py startproject django_test

5. 「settings.py」파일 수정

INSTALLED_APPS = ( 'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'doj', # 이부분을 추가함.
)



6. Hello Django페이지를 작성
  - hello_view.py

'''
Created on 2010/02/01

@author: babukuma
'''

from django.http import HttpResponse

def hello(request):
"""Hello Django"""
message = "<html><body>Hello, Django on Jython!</body></html>"
return HttpResponse(message)


7. 「urls.py」파일을 수정

from django.conf.urls.defaults import *
from hello_view import * # 추가부분

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('',
# Example:
# (r'^django_test/', include('django_test.foo.urls')),

# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'hello/$', hello), # 추가부분

# Uncomment the next line to enable the admin:
# (r'^admin/', include(admin.site.urls)),
)



8. 테스트용 서버에서 확인
  - # jython manager.py runserver



9. war파일 작성
  - # jython manager.py war
  - 「django_test.war」파일이 작성된다.

10. Tomcat에서 테스트


Tomcat위에서도 Django가 문제없이 돌아가는 것 같다.

2010年1月29日金曜日

[Android] Custom Component - DateTimePicker


DatePicker와 TimePicker를 이용하여 간단하게 만들어본 DateTimePicker

DatePicker와 TimePicker는 존재하지만, 왜인지 날짜와 시간을 한번에 설정할 수 있는 컨포넌트가 안보인다.
그래서 간단하게 만들어봤다.

우선 레이아웃 구성 [res/layout/datetime_picker.xml]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:gravity="center">
<DatePicker android:id="@+id/_babukuma_datetime_picker_date_picker"
android:layout_width="wrap_content" android:layout_height="wrap_content"></DatePicker>
<CheckBox android:id="@+id/_babukuma_datetime_picker_enable_time"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="enable Time"></CheckBox>
<TimePicker android:id="@+id/_babukuma_datetime_picker_time_picker"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TimePicker>
</LinearLayout>


다음은 몇가지 필요할 것 같은 Attribute를 정의 [res/values/attrs.xml]

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DataTimePicker">
<attr name="enableTime" format="boolean" />
<attr name="year" format="integer" />
<attr name="month" format="integer" />
<attr name="day" format="integer" />
<attr name="hour" format="integer" />
<attr name="minute" format="integer" />
</declare-styleable>
</resources>


다음으로는 DateTimePicker의 소스다. [DateTimePicker.java]
대부분이 기존의 DatePicker와 TimePicker가 가지고 있는 메소드를 그대로 사용하도록 했고,
단지 하단의 TimePicker를 CheckBox를 이용해서 활성화/비활성화 시킨 것 뿐이다.

/**
* http://babukuma.com
*/
package com.babukuma.android.view;

import java.util.Calendar;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.TimePicker;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.DatePicker.OnDateChangedListener;
import android.widget.TimePicker.OnTimeChangedListener;

import com.babukuma.android.test.R;

/**
* @author babukuma
*/
public class DateTimePicker extends LinearLayout {
/**
* @author babukuma
*/
public interface OnDateTimeChangedListener {
/**
* @param view
* The view associated with this listener.
* @param year
* The year that was set.
* @param monthOfYear
* The month that was set (0-11) for compatibility with
* {@link java.util.Calendar}.
* @param dayOfMonth
* The day of the month that was set.
* @param hourOfDay
* The current hour.
* @param minute
* The current minute.
*/
void onDateTimeChanged(DateTimePicker view, int year, int monthOfYear,
int dayOfMonth, int hourOfDay, int minute);
}

private OnDateTimeChangedListener onDateTimeChangedListener;

private final DatePicker datePicker;
private final CheckBox enableTimeCheckBox;
private final TimePicker timePicker;

/**
* @param context
*/
public DateTimePicker(final Context context) {
this(context, null);
}

/**
* @param context
* @param attrs
*/
public DateTimePicker(final Context context, final AttributeSet attrs) {
super(context, attrs);

// Layout
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.datetime_picker, this, true);

// Attribute
Calendar calendar = Calendar.getInstance();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.DataTimePicker);
final int _currentYear = a.getInt(R.styleable.DataTimePicker_year,
calendar.get(Calendar.YEAR));
final int _currentMonth = a.getInt(R.styleable.DataTimePicker_month,
calendar.get(Calendar.MONTH));
final int _currentDay = a.getInt(R.styleable.DataTimePicker_day,
calendar.get(Calendar.DAY_OF_MONTH));
final int _currentHour = a.getInt(R.styleable.DataTimePicker_hour,
calendar.get(Calendar.HOUR_OF_DAY));
final int _currentMinute = a.getInt(R.styleable.DataTimePicker_minute,
calendar.get(Calendar.MINUTE));

// DatePicker
datePicker = (DatePicker) findViewById(R.id._babukuma_datetime_picker_date_picker);
datePicker.init(_currentYear, _currentMonth, _currentDay,
new OnDateChangedListener() {

@Override
public void onDateChanged(final DatePicker view,
final int year, final int monthOfYear,
final int dayOfMonth) {
if (onDateTimeChangedListener != null) {
onDateTimeChangedListener.onDateTimeChanged(
DateTimePicker.this, year, monthOfYear,
dayOfMonth, timePicker.getCurrentHour(),
timePicker.getCurrentMinute());
}
}
});

// Enable Time checkbox
enableTimeCheckBox = (CheckBox) findViewById(R.id._babukuma_datetime_picker_enable_time);
enableTimeCheckBox
.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
timePicker.setEnabled(isChecked);
timePicker.setVisibility((enableTimeCheckBox
.isChecked() ? View.VISIBLE : View.INVISIBLE));
}
});

// TimePicker
timePicker = (TimePicker) findViewById(R.id._babukuma_datetime_picker_time_picker);
timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {
@Override
public void onTimeChanged(final TimePicker view,
final int hourOfDay, final int minute) {
if (onDateTimeChangedListener != null) {
onDateTimeChangedListener.onDateTimeChanged(
DateTimePicker.this, datePicker.getYear(),
datePicker.getMonth(), datePicker.getDayOfMonth(),
hourOfDay, minute);
}
}
});
timePicker.setCurrentHour(_currentHour);
timePicker.setCurrentMinute(_currentMinute);
timePicker.setEnabled(enableTimeCheckBox.isChecked());
timePicker.setVisibility((enableTimeCheckBox.isChecked() ? View.VISIBLE
: View.INVISIBLE));
}

public void setOnDateTimeChangedListener(
OnDateTimeChangedListener onDateTimeChangedListener) {
this.onDateTimeChangedListener = onDateTimeChangedListener;
}

public void updateDateTime(int year, int monthOfYear, int dayOfMonth,
int currentHour, int currentMinute) {
datePicker.updateDate(year, monthOfYear, dayOfMonth);
timePicker.setCurrentHour(currentHour);
timePicker.setCurrentMinute(currentMinute);
}

public void updateDate(int year, int monthOfYear, int dayOfMonth) {
datePicker.updateDate(year, monthOfYear, dayOfMonth);
}

public void setIs24HourView(final boolean is24HourView) {
timePicker.setIs24HourView(is24HourView);
}

public int getYear() {
return datePicker.getYear();
}

public int getMonth() {
return datePicker.getMonth();
}

public int getDayOfMonth() {
return datePicker.getDayOfMonth();
}

public int getCurrentHour() {
return timePicker.getCurrentHour();
}

public int getCurrentMinute() {
return timePicker.getCurrentMinute();
}

public boolean enableTime() {
return enableTimeCheckBox.isChecked();
}
}


나중에 좀더 속성을 추가하고, 살을 더 붙이면 쓸만할 것 같긴한데,
우선은 이것으로 대충 완성. 일단은 쓸 수는 있어보이니까.

다음은 간단하게 테스트.


테스트용 레이아웃 [res/layout/datetime_picker_test.xml]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/datetime_picker_activity_text_view"
android:layout_width="fill_parent" android:layout_height="48dip"
android:textColor="#000000" android:background="#CCCCCC"
android:gravity="center" android:text="time"></TextView>
<com.babukuma.android.view.DateTimePicker
android:layout_width="wrap_content" android:layout_height="wrap_content"
year="2010" month="1" day="29" hour="14" minute="1" startYear="2000"
android:id="@+id/datetime_picker_activity_datetime_picker">
</com.babukuma.android.view.DateTimePicker>
</LinearLayout>


그리고 테스트용 소스 [DateTimePickerActivity.java]

package com.babukuma.android.test;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import com.babukuma.android.view.DateTimePicker;
import com.babukuma.android.view.DateTimePicker.OnDateTimeChangedListener;

public class DateTimePickerActivity extends Activity {
final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.datetime_picker_test);

final TextView timeView = (TextView) findViewById(R.id.datetime_picker_activity_text_view);
final DateTimePicker dateTimePicker = (DateTimePicker) findViewById(R.id.datetime_picker_activity_datetime_picker);
dateTimePicker
.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
@Override
public void onDateTimeChanged(DateTimePicker view,
int year, int monthOfYear, int dayOfMonth,
int hourOfDay, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, monthOfYear, dayOfMonth, hourOfDay,
minute);
timeView.setText(dateFormat.format(calendar.getTime()));
}
});

Calendar calendar = Calendar.getInstance();
calendar.set(dateTimePicker.getYear(), dateTimePicker.getMonth(),
dateTimePicker.getDayOfMonth(),
dateTimePicker.getCurrentHour(), dateTimePicker
.getCurrentMinute());
timeView.setText(dateFormat.format(calendar.getTime()));
}
}


다음에는 DateTimePickerDialog를 만들어봐야겠다.