Friday, August 28, 2015

Android SQLite 예제

package com.example.xxx;

import java.util.ArrayList;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class BookmarkDB extends SQLiteOpenHelper {
 private final static String TAG="BookmarkDB";

 public BookmarkDB(Context context, String name, CursorFactory factory,
   int version) {
  super(context, name, factory, version);
  // TODO Auto-generated constructor stub
 }

 @Override
 public void onCreate(SQLiteDatabase db) {
  // TODO Auto-generated method stub
  db.execSQL("CREATE TABLE bookmark (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT, page INTEGER);");
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  // TODO Auto-generated method stub

 }

 public void querySQL(String sql) {
  Log.d(TAG,"querySQL sql="+sql);
  SQLiteDatabase db = getWritableDatabase();
  db.execSQL(sql);
  db.close();
 }

 ArrayList<Integer> getPages(String path) {
  SQLiteDatabase db = getReadableDatabase();
  Cursor cursor = db.rawQuery("SELECT page FROM bookmark WHERE path='"
    + path + "'", null);

  ArrayList<Integer> pageList = new ArrayList<Integer>();
  while (cursor.moveToNext()) {
   int page = cursor.getInt(0);
   Log.d(TAG, "getPages page="+page);
   pageList.add(page);
  }
  db.close();
  return pageList;
 }

}

Tuesday, August 25, 2015

Android 키보드 입력시 액티비티 윈도우 리사이즈 방지하기

안드로이드에서 키보드 입력시 액티비티의 전체윈도우가 리사이즈 되는 경우가 있다.
그럴 경우 다음과 같으 AndroidManifest.xml에서 다음과 같이 정의하면 키보드가 나오더라도 리사이즈 되지 않는다.

    <application
...
        android:windowSoftInputMode="adjustPan" >

Monday, August 24, 2015

Android ViewPager + PagerTabStrip 예제

안드로이드에서 메인화면에 탭을 구현하는 방법은 여러가지가 있다.

첫번째는 ActionBar에 NAVIGATION_TABS로 모드를 설정하는 방법이고,
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

두번째는 컨텐츠뷰에 PageTabStrip이라는 UI를 사용하는 방법인데, 이 방법은 무조건 ViewPager가 있어야 한다.

일단 이전글의 ViewPager 예제를 완성해놓고, 여기다가 조금만 추가하면 PageTabStrip을 만들수 있다.
http://duongame.blogspot.kr/2015/08/android-viewpager.html

activity_main.xml
<android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <android.support.v4.view.PagerTabStrip
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:textColor="#000000" />
    </android.support.v4.view.ViewPager>

 private class PagerAdapter extends FragmentStatePagerAdapter {
  // Tab Titles
  private String tabtitles[] = new String[] { "Tab1", "Tab2", "Tab3", "Tab4", "Tab5" };

...
  // PageTabStrip
  @Override
  public CharSequence getPageTitle(int position) {
   return tabtitles[position];
  }

 }

Android ViewPager 예제 (FragmentStatePagerAdapter 사용)

Android에서 많이 사용하는 ViewPager의 예제이다. ViewPager를 사용하려면 필수적으로 FragmentActivity를 사용해야하는데, ActionBarActivity를 사용하고 있으면 무시하고 ActionBarActivity를 그대로 사용해도 된다.(FragmentActivity를 상속받고 있기 때문이다.)

public class MainActivity extends ActionBarActivity {
 private ViewPager mViewPager;
 private PagerAdapter mPagerAdapter;
 private class PagerAdapter extends FragmentStatePagerAdapter {
  public PagerAdapter(FragmentManager fm) {
   super(fm);
  }
  @Override
  public Fragment getItem(int position) {
                        // 해당하는 page의 Fragment를 생성합니다.
   return PageFragment.create(position);
  }
  @Override
  public int getCount() {
   return 5;  // 총 5개의 page를 보여줍니다.
  }

 }

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

  mViewPager = (ViewPager) findViewById(R.id.pager);
  mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
  mViewPager.setAdapter(mPagerAdapter);
 }
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }
}

public class PageFragment extends Fragment {
 private int mPageNumber;
 public static PageFragment create(int pageNumber) {
  PageFragment fragment = new PageFragment();
  Bundle args = new Bundle();
  args.putInt("page", pageNumber);
  fragment.setArguments(args);
  return fragment;
 }
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  mPageNumber = getArguments().getInt("page");
 }
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_page, container, false);
  ((TextView) rootView.findViewById(R.id.number)).setText(mPageNumber + "");
  return rootView;
 }
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.viewpagerexample.MainActivity" >
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
    </android.support.v4.view.ViewPager>
</RelativeLayout>

fragment_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:id="@+id/number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/hello_world" />
</RelativeLayout>

https://github.com/duongame/ViewPagerExample1

Friday, August 21, 2015

SVN에서 Git으로 마이그레이션하기

기존에 사용하던 SVN이 네이버 개발자센터의 SVN이었다. 그런데 네이버 개발자센터의 관리자 페이지의 지원이 너무 형편없고, SVN은 다운이 잦았으며, Mac에서 SVN 클라이언트인 Versions는 유료 프로그램임에도 불구하고 기능이 너무 없어서 이번기회에 Git으로 이전하기로 하였다.

그래서 기존에 사용하던 SVN을 Git으로 안전하게 이전하는 방법에 대해서 정리해 보았다. Git 저장소는 5인이하 무료인 www.bitbucket.org를 사용하고, 클라이언트는 SourceTree를 사용하기로 결정했다.

일단 윈도우를 기준으로 GitHub 클라이언트와 SourceTree 윈도우 버전을 설치한다. 그런다음에 GitShell을  연다.

다음과 같이 GitShell을 열고 난 다음, cd "c:\git"과 같이 git 프로젝트를 clone할 폴더를 만들어서 이동하자.

그리고 다음과 같이 SVN 저장소 주소를 입력한다.
git svn clone https://dev.naver.com/svn/capture capture -s
이때 권한을 요구하게 되는데 p를 누르고 아이디, 암호를 입력을 입력하자.

그러면 git이 svn을 clone할 것이다. 그런 다음에 다음과 같이 작업공간 열기를 통해서 clone한 svn 저장소를 오픈한다.

추가된 작업 공간에 git을 연결하자. 이때는 git에서 clone하는 저장소 주소를 복사해서 입력하면 push가 된다.


다음에는 git 저장소로 push를 해야 되는데 앞에서 저장했던 git 저장소 주소로 push가 된다.

git으로 push가 끝나면 svn의 버전과 태그가 다 살아있는 것을 볼 수 있다.

Sunday, August 16, 2015

Android ActionBar Up 버튼 추가하기

안드로이드의 액션바에서 상위 액티비티로 이동하는 Up버튼을 추가해보자.
http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp

기본적으로는 위의 링크를 기본으로 하는데, NavUtils를 사용하는 것보다 현재 액티비티를 finish()하는게 내가 사용하는 목적에는 더 적합했다.

하위 액티비티에서:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
... 
// Up 버튼
  actionBar.setDisplayHomeAsUpEnabled(true);
...
}
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  // Respond to the action bar's Up/Home button
  case android.R.id.home:
   finish();   return true;
  default:
   return super.onOptionsItemSelected(item);
  }
 }

AndroidManifext.xml 에서:
        <activity
            android:name=".ViewerActivity"
            android:parentActivityName=".MainActivity"
            android:theme="@style/ViewerTheme" >
            <!-- Parent activity meta-data to support 4.0 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainActivity" />
        </activity>


Java 트위터 API twitter4j 사용하기(OAuth)

트위터 API로 트위터에 접속하는 방법을 설명한다. 언어는 JSP이고 라이브러리는 twitter4j이다.

이클립스 설정은 아래의 글을 확인하면 된다.
http://duongame.blogspot.kr/2015/08/blog-post_16.html

개괄적인 트위터 API의 예제에 대한것은 트랙백을 참고하고, 소스위주로 설명한다. 아직은 서버가 없으므로 localhost에서 톰캣으로 한다.

먼저 트위터 개발자 계정(http://dev.twitter.com)에 가서 create an app을 한다.

application을 만들었으면 거기서 customer key와 customer secret을 복사하여 twitter.java에 다음과 같이 입력한다.

public class TwitterClient {
 public TwitterClient() {
  System.out.println("TwitterClient.TwitterClient");
 }
 Twitter twitter;
 RequestToken requestToken = null;
 AccessToken accessToken = null;

 final String CONSUMER_KEY = "IAsSj*****************";
 final String CONSUMER_SECRET = "MKxMB**********************************";

그리고 Callback URL을 등록을 한다. localhost는 등록이 되지 않으므로 127.0.0.1:8080으로 등록하자. 위에서는 http://127.0.0.1:8080/twt/redirect.jsp로 등록을 했다.
다음 이클립스 화면과 같이 TwitterClient.java와 index.jsp, main.jsp, signin.jsp, redirect.jsp를 만들자.

index.jsp는 그냥 main.jsp로 redirect하는 역할만 한다. 나중에 메인 페이지를 다른것으로 만들기 위해서 만들어 놨다.
<script>
location.href = "main.jsp";
</script>

main.jsp는 signin.jsp에 링크만 걸어놨다.
<a href="signin.jsp">트위터</a>

signin.jsp는 requestToken을 만들고 authorizationURL을 만들어서 twitter로 redirect를 한다. requestToken을 만드는 과정은 twitter4j에 customer key와 customer secret을 넘겨주면 완성된다. redirect.jsp에 넘겨주기 위해 생성된 requestToken과 TwitterClient 객체를 session에 저장한다.

[signin.jsp]
<%@page import="twt.TwitterClient"%>
<%@page import="twitter4j.auth.RequestToken"%>
<%
TwitterClient tw = new TwitterClient();
RequestToken token = tw.getRequestToken();
String authUrl = token.getAuthorizationURL();
session.setAttribute("requestToken", token);
session.setAttribute("tw", tw);
%>
<script>
location.href = "<%=authUrl%>";
</script>

[TwitterClient.java]
 public RequestToken getRequestToken()
 {
  twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
  requestToken = null;
  try {
   requestToken = twitter.getOAuthRequestToken();
  }
  catch(TwitterException te) {
   te.printStackTrace();
  }

  System.out.print("TwitterClient.getRequestToken: ");
  System.out.print(requestToken.getToken() + " ");
  System.out.println(requestToken.getAuthorizationURL());
  return requestToken;
 }

그러면 이제 최종적으로 redirect.jsp를 보자. 이 페이지는 twitter 서버에서 등록해둔 호출 서버로 redirect page로 redirect되는데 우리가 127.0.0.1로 해놓았기 때문에 다시 우리 데스크탑 서버로 돌아온다.
parameter중에서 oauth_token이 그 응답이다. 이것을 가지고 아까 저장해준 TwitterClient와 RequestToken을 꺼내서 getAccessToken을 하면 접속이 완료된다. 그후에 타임라인을 테스트하는 함수인 printStatuses를 부르면 이클립스 콘솔창에 현재 타임라인이 나타나는 것을 볼수 있다.
[rediect.jsp]
<%@page import="twt.TwitterClient"%>
<%@page import="twitter4j.auth.AccessToken"%>
<%@page import="twitter4j.auth.RequestToken"%>
<%
// twitter에서 넘겨줌
String oauth_token = request.getParameter("oauth_token");
// signin.jsp에서 저장한 것들
RequestToken token = (RequestToken)session.getAttribute("requestToken");
TwitterClient tw = (TwitterClient)session.getAttribute("tw");
tw.getAccessToken(oauth_token, token);
tw.printStatuses();
%>

[TwitterClient.java]
 public AccessToken getAccessToken(String oauthToken, RequestToken requestToken)
 {
  if(requestToken == null) {
   System.out.print("requestToken == null");
  }
  if(oauthToken == null) {
   System.out.print("oauthToken == null");
  }

  twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
  System.out.println("TwitterClient.getAccessToken: ");

  if(requestToken.getToken().equals(oauthToken)) {
   try {
    System.out.print("requestToken: ");
    System.out.print(requestToken.getToken() + " ");
    System.out.println(requestToken.getTokenSecret());
    accessToken = twitter.getOAuthAccessToken(requestToken);
    //accessToken = twitter.getOAuthAccessToken(token.getToken(), token.getTokenSecret());
    twitter.setOAuthAccessToken(accessToken);
  
    System.out.print("accessToken: ");
    System.out.print(accessToken.getToken() + " ");
    System.out.println(accessToken.getTokenSecret());
   }
   catch(TwitterException te) {
    te.printStackTrace();
   }
  }
  else {
   System.out.println("oauth_token error");
  }
  return accessToken;
 }
 public void printStatuses()
 {
  ResponseList<Status> statuses;  
  Paging page = new Paging();
  page.count(20);
  page.setPage(1);  

  try {  
   statuses = twitter.getHomeTimeline(page);
   for (Status status : statuses) {   
    //status.getId()   
    System.out.println(status.getUser().getScreenName() + ":" + status.getText());    
    //status.getUser().getScreenName()    
    //status.getUser().getURL()   
    //status.getText()   
    //status.getCreatedAt()   
    //status.getUser().getProfileImageURL()   
    //status.getSource()
   } 
  }
  catch(TwitterException te) {
   te.printStackTrace();
  }
 }

이제 실행을 해보자. 서버의 hostname은 반드시 127.0.0.1이고 port는 8080이어야 한다. hostname이 localhost라면 session문제가 생기므로 새로운 서버를 만들기 바란다.

signin.jsp를 실행한 화면

애플리케이션 승인후에 redirect.jsp를 부르는 화면

redirect.jsp로 와서 timeline을 display하는 화면

Java 이클립스 톰캣연동

이클립스와 톰캣연동은 수많은 강좌가 있지만 최신버전이 대한 강좌는 없어서 작성해 보았다. 이걸 왜 작성하려 했냐면 트위터가 원하는 기능이 너무 없어서 트위터 API를 만들어서 구글 앱엔진에 올리기 위해서였다. 그래서 이후에는 트위터 연동도 할거고 구글 앱엔진에도 올릴 예정이다.

이클립스 - 버전 3.7 Indigo http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/indigor

이클립스는 JSP를 연동하려면 무조건 Java EE 버전을 다운 받아야 한다. 안그러면 다른 플러그인(WTP)을 받아야 하는 수고를 해야한다.

톰캣 - 7.0 http://tomcat.apache.org/download-70.cgi
먼저 이클립스를 다음과 같이 실행한다. 실행한 다음 아래의 탭에서 [Server] – [New] – [Server]를 선택한다. ([File] – [New] – [Other]에서 시작해도 마찬가지다.)


다음과 같이 Define a New Server창이 나타나는데 여기서 Tomcat v7.0 서버를 고른다.(Basic HTTP Server를 고르면 안됨) 

Tomcat 7.0이 설치된 위치를 입력하자. 

위치를 잘못 입력하면 찾을 수 없다고 친절히 안내해 준다.

아래와 같이 Tomcat v7.0 Server가 localhost에 생겼다.

이제 이클립스에서 Tomcat을 실행시켜보자. 다음과 같은 방화벽이 나오면 허용해주자.

허용하자마자 기존에 백그라운드로 톰캣이 돌고 있으면 포트충돌이 있다고 안내해준다.

백그라운드 톰캣은 중지해주자.

그러면 다시 자바를 허용하라고 방화벽 메세지가 나온다. 허용하면 톰캣이 실행된다.

이제는 테스트 프로젝트를 만들어 보자.
[File] – [New] – [Dynamic Web Project]를 선택하고 Project name으로 “testjsp”를 입력한다.

다음과 같이 Server 밑에 testjsp가 만들어졌다.

그리고 바로 Run을 해보자. Run on Server를 선택하자.

어떤 서버에서 실행할 것인가를 묻는다. 여러개를 만들수 있다. 아까 만들어진 Tomcat 7.0 서버를 선택하자. 

이제는 프로젝트의 파일들를 선택하는란이다. Finish를 하자.

서버를 시작할때 기존에 실행중인 서버를 재시작 할것인가를 묻는다. 일반적으로 재시작 안해도 되는데 매번 재시작을 할수도 있다. 아무거나 선택하자.

실행하면 이클립스 안에서 웹이 뜨는데 에러가 났다. http://localhost:8080/testjsp/index.jsp우리는 index.jsp가 없기 때문에 에러가 난 것이었다.


이제 index.jsp를 만들어 주자. 다음과 같이 out.println해보자.

실행을 시키면 out.println이 정상적으로 된다.

이상 이클립스와 톰캣 JSP가 연동이 되었다.

Python PyDev 설치하기

Python 으로 작업을 하던 도중에 Python IDLE이 너무 불편하여 PyDev를 사용해 보기로 하였다. Eclipse 플러그인으로 동작하는 것인데 기존에 이클립스를 썩 좋아하지 않았지만, 파이썬은 툴이 거의 없어서 플러그인으로 사용하니 매우 만족할만한 개발환경을 보여주었다.
설치법은 한번에 설치가 안되고 여러번 나누어서 설치하여야 모든 것이 설치가 된다.


Install New Software를 누른다. 

다음과 같이 PyDev라는 Repository를 Add하고 http://pydev.org/updates를 추가한다. 


모든 항목을 선택 후에 Next.

전부 선택하면 다음과 같이 에러가 난다. 그래서 하나씩 설치하자. 

메인인 PyDev for Eclipse를 먼저 설치하자. 




Apanata를 신뢰한다고 해준다. 

이클립스를 재시작하자. 


그리고 나머지를 같은 방식으로 설치하는데 Django를 가장 나중에 설치한다. 








다시 이클립스를 재시작한다. 




그리고 파이썬 관련 라이브러리도 선택해 준다. Pydev - Interpreter - Python항목이다. 



다음과 같이 PyTest.py를 만들었다. 간단히 print문을 입력하고 F11을 누르면 Console(Output)창에 결과가 나온다. 

다음은 나름대로 색깔을 설정할 수 있다. 

스펠체크를 싫어하면 끄자.