'분류 전체보기'에 해당되는 글 168건
- 2011.11.30 오늘 먹은 스테이크
- 2011.11.24 구글 앱 엔진 파이썬 런쳐가 실행 오류
- 2011.11.23 안드로이드 트위터 연동 번외편(이미지 업로드) 2
- 2011.11.21 android OpenCV 빌드 중 cannot find - lcxcore 에러 해결 방법
- 2011.11.17 이클립스에서 코드 정렬
- 2011.11.15 이클립스에서 Column 선택 지원 및 Code Align을 지원하는 플로그인
- 2011.11.06 해님 달님
- 2011.11.04 자바에서 콜백 처리를 위한 리스너
- 2011.11.03 Android 게임 강좌 - Cocos2d로 슈팅 게임 만들기 서문
- 2011.11.03 C2DM 서비스 플로우 확장
- 2011.11.02 C2DM 소스(안드로이드에서 푸시 서버를 사용하자.)
- 2011.11.02 페이스북에게 사과를 받다.
- 2011.11.02 C2DM 간략 시퀀스
- 2011.11.01 참 뭐 같은 페이스북
- 2011.10.31 안드로이드 구글 맵 연동 4
- 2011.10.31 ADT 14 이후 Google APIs 항목이 설치되지 않는 문제 해결 8
- 2011.10.31 구글 맵 인증서 키 md5로 얻기
- 2011.10.30 알라딘 중고서점을 다녀와서…
- 2011.10.28 안드로이드 트위터 정리
- 2011.10.28 안드로이드 트위터 추가사항
- 2011.10.28 안드로이드 트위터 연동 최종
- 2011.10.27 안드로이드 트위터 연동 #2
- 2011.10.27 OAuth 용어 정리
- 2011.10.27 안드로이드 트위터 연동 #1
- 2011.10.27 Android Facebook 연동
- 2011.10.21 MacBook Pro 설치 후 Boot Camp 제어판에서 해야 할 일
- 2011.10.19 cocos2d for android - 서서히 빨라졌다 느려졌다 다시 빨라졌다 이동
- 2011.10.19 cocos2d for android - 점점 빠르게, 점점 느리게 레이어를 이동
- 2011.10.19 cocos2d for android - 클린업
- 2011.10.19 cocos2d for android - 태그로 노드 제어
지난번 최종 소스를 올렸지만 정작 중요한 이미지 업로드 기능이 빠져 있으므로 이번에 추가하였습니다.
트윗픽을 연동하여 올리는 것이 아닌 순수 트위터만의 API를 이용하여 전송하는 예제 입니다.
1: package com.coolsharp.test;
2:
3: import java.io.File;
4: import java.util.List;
5:
6: import twitter4j.Paging;
7: import twitter4j.Status;
8: import twitter4j.Twitter;
9: import twitter4j.TwitterException;
10: import twitter4j.TwitterFactory;
11: import twitter4j.auth.AccessToken;
12: import twitter4j.auth.RequestToken;
13: import twitter4j.conf.Configuration;
14: import twitter4j.conf.ConfigurationBuilder;
15: import twitter4j.media.ImageUpload;
16: import twitter4j.media.ImageUploadFactory;
17: import android.app.Activity;
18: import android.content.Intent;
19: import android.net.Uri;
20: import android.os.Bundle;
21: import android.util.Log;
22: import android.view.View;
23: import android.widget.Button;
24:
25: /**
26: * @author coolsharp
27: *
28: */
29: public class TwitterApp extends Activity {
30:
31: /**
32: * Twitter instance object
33: */
34: private Twitter twitter;
35: private AccessToken acToken = null;
36: private RequestToken rqToken = null;
37: private Status status = null;
38: String access_token = "";
39: String access_token_secret = "";
40:
41: private static final String CONSUMER_KEY = "";
42: private static final String CONSUMER_SECRET = "";
43: private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
44: private static final String ACCESS_TOKEN_SECRET = "ACCESS_TOKEN_SECRET";
45: public static Uri CALLBACK_URL = Uri.parse("wefu://twitter");
46:
47: /** 생성. */
48: @Override
49: public void onCreate(Bundle savedInstanceState) {
50: super.onCreate(savedInstanceState);
51: setContentView(R.layout.main);
52:
53: // 트위터 인스턴스 얻기
54: twitter = new TwitterFactory().getInstance();
55: // 컨슈머 키 대입
56: twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
57:
58: // 버튼 리스너 등록
59: Button btn = (Button) findViewById(R.id.btnAuthorize);
60: btn.setOnClickListener(new Button.OnClickListener() {
61: public void onClick(View v) {
62: try {
63: File file = new File("/sdcard/test.jpg");
64: if (file.exists())
65: writePicTwitter("test", file);
66: // if (null != acToken) getList();
67: } catch (TwitterException e) {
68: // TODO Auto-generated catch block
69: e.printStackTrace();
70: } // 토큰 발행
71: }
72: });
73: }
74:
75: /**
76: * 트위터 글 쓰기
77: * @return
78: * @throws TwitterException
79: */
80: private boolean writeTwitter(String content) throws TwitterException {
81: boolean res = false;
82:
83: getToken(); // 토큰 읽기
84:
85: if (null != acToken) {
86: // 트위터 글쓰기
87: // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
88: // dateFormat.format(new Date(System.currentTimeMillis())).toString();
89: status = twitter.updateStatus(content);
90: }
91:
92: return res;
93: }
94:
95: /**
96: * 트위터 글 쓰기
97: * @return
98: * @throws TwitterException
99: */
100: private boolean writePicTwitter(String content, File file) throws TwitterException {
101: boolean res = false;
102:
103: getToken(); // 토큰 읽기
104:
105: if (null != acToken) {
106: ConfigurationBuilder builder = new ConfigurationBuilder();
107: builder.setOAuthConsumerKey(CONSUMER_KEY);
108: builder.setOAuthConsumerSecret(CONSUMER_SECRET);
109: builder.setOAuthAccessToken(access_token);
110: builder.setOAuthAccessTokenSecret(access_token_secret);
111: builder.setMediaProvider("TWITTER");
112:
113: Configuration conf = builder.build();
114:
115: ImageUpload imageUpload = new ImageUploadFactory(conf)
116: .getInstance();
117:
118: try {
119: imageUpload.upload(file, content);
120: } catch (TwitterException e) {
121: e.printStackTrace();
122: }
123: }
124:
125: return res;
126: }
127:
128: /**
129: * 토큰 얻기
130: * @return
131: * @throws TwitterException
132: */
133: private void getToken() throws TwitterException {
134: // 토큰이 발행 되지 않았으면
135: if (null == acToken) {
136: // 최근 사용한 토큰 읽기
137: access_token = Util.getSharedPreferencesValue(this, "coolsharp", MODE_PRIVATE, ACCESS_TOKEN);
138: access_token_secret = Util.getSharedPreferencesValue(this, "coolsharp", MODE_PRIVATE, ACCESS_TOKEN_SECRET);
139:
140: // 토큰 값이 모두 있으면
141: if ((!access_token.equals("")) && (!access_token_secret.equals(""))) {
142: // 기존 토큰 값으로 토큰 발행
143: acToken = new AccessToken(access_token, access_token_secret);
144: twitter.setOAuthAccessToken(acToken);
145: }
146: else {
147: // 인증 시작
148: startAuth();
149: }
150: } else {
151: }
152: }
153:
154: /**
155: * 인증 시작
156: * @throws TwitterException
157: */
158: private void startAuth() throws TwitterException {
159: rqToken = twitter.getOAuthRequestToken(CALLBACK_URL.toString());
160: startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(rqToken.getAuthorizationURL())));
161: }
162:
163: /**
164: * @throws TwitterException
165: */
166: private void getList() throws TwitterException {
167: List<Status> statuses;
168: Paging page = new Paging();
169: page.count(20);
170: page.setPage(1);
171: statuses = twitter.getHomeTimeline(page);
172: for (Status status : statuses) {
173: Log.i("coolsharp", status.getText());
174: }
175: }
176:
177: /* (non-Javadoc)
178: * 인텐트 생성
179: * @see android.app.Activity#onNewIntent(android.content.Intent)
180: */
181: protected void onNewIntent(Intent intent) {
182: super.onNewIntent(intent);
183: Uri uri = intent.getData();
184: if (uri != null && CALLBACK_URL.getScheme().equals(uri.getScheme())) {
185: String oauth_verifier = uri.getQueryParameter("oauth_verifier");
186: try {
187: acToken = twitter.getOAuthAccessToken(rqToken, oauth_verifier);
188: access_token = acToken.getToken();
189: access_token_secret = acToken.getTokenSecret();
190: Util.setSharedPreferencesValue(this, "coolsharp", MODE_PRIVATE, ACCESS_TOKEN, access_token);
191: Util.setSharedPreferencesValue(this, "coolsharp", MODE_PRIVATE, ACCESS_TOKEN_SECRET, access_token_secret);
192: } catch (TwitterException e) {
193: Log.e("coolsharp", e.getMessage());
194: }
195: }
196: }
197: }
android에서 OpenCV 빌드 중 cannot find – lcxcore 에러가 나온다면 다음과 같이 해결 할 수 있다.
android.mk 파일을 수정한다.
LOCAL_LDLIBS := -L $(SYSROOT)/usr/lib -l dl -l log \
-L "D:\OpenCV-Android\obj\local\armeabi" \
-l cxcore -l cv -l cvaux -l cvml -l cvhighgui
TARGET-OUT을 수정한다.
이상이다.
소스 코드를 작성 하다 보면 정렬이 안된 코드는 가독성이 떨어져 향 후 유지보수에 불편함을 줍니다.
물론 개의치 않는 개발자분들도 많으시겠지만 가급적이면 소스 코드는 들여쓰기, 내어쓰기, 정렬이 잘 되어 있는 코드가 깔끔하고 보기에도 좋겠지요.
다음과 같은 경우 유용하게 사용될 수 있습니다.
정렬이 안된 소스 코드
1: public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
2: public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
3: public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
4: public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
5: public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
6: public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
7: public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
정렬이 된 소스 코드
1: public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
2: public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
3: public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
4: public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
5: public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
6: public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
7: public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
대부분의 개발툴이 코드 정렬 기능을 근본적을 지원해 주시 않고 있습니다.
때문에 각 개발툴은 플러그인을 통해 위와 같은 기능을 사용할 수 있습니다.
비주얼 스튜디오는
Visual Studio Code Align
편을 보시면 플러그인을 통한 코드 정렬을 쉽게 할 수 있습니다.
오늘의 주제는 이클립스이므로 이클립스에서 코드 정렬의 기능을 설명 드리도록 하겠습니다.
이클립스에서 코드 정렬을 하려면 플러그인을 설치해야 합니다.
플러그인 설치
이클립스 플러그인을 설치합니다.
기본적으로 이클립스 사용법을 아시는 분들을 상대로 한 강좌이기 때문에
설치 방법을 따로 설명 드리지는 않겠습니다.
플러그인 주소 : http://columns4eclipse.sourceforge.net/updates/
사용법
설치가 끝나면 재 시작을 한 후 프로젝트를 오픈합니다.
정렬을 원하는 코드를 블록 지정 합니다.
단축키 Shift + alt + K, A를 누릅니다.
정렬을 원하는 구분자를 입력 후 OK를 누르면 코드 정렬이 자동으로 진행 됩니다.
비가 많이 온다고 기상청에서 뻥을 쳐서 긴장하고 있었는데 언제나 그렇듯이 오늘도 맑음이네요.
덕분에 편하게 유치원을 다녀올 수 있었습니다.
해마다 유치원에서는 전시회를 합니다.
이번 주제는 해님 달님이네요.
호랑이들이 모두 웃고 있네요.
우리 첫째가 만든 바나나우유 호랑이 입니다.
뚱뚱한 바나나 우유통을 사가야 한다고 해서 회사에서 퇴근 후 마트에 들려 사온 기억이 나네요.
아이들이 만든 해님달님 동화 책 입니다.
표지를 그려서 전시했네요.
내용을 릴레이로 이어가 결국 예쁜 동시가 완성 되었네요.
추가로 찍은 5세반 어린이들의 작품입니다.
자바에서 콜백 처리를 하기 위해서는 인터페이스를 사용해야 한다.
인터페이스는 아래처럼 정의 할 수 있다.
1: interface onTapCallback {
2: void onTap(GeoPoint p, MapView mapView);
3: }
호출할 함수 원형을 인터페이스에서 정의 하였으면 인터페이스 변수를 선언해야 한다.
1: private onTapCallback cbOnTap = null;
인터페이스 변수를 선언하였다면 인터페이스 변수를 외부에서 할당 할 수 있게 set 함수를 만들어 준다.
1: public void setOnTapCallback(onTapCallback cb) {
2: cbOnTap = cb;
3: }
실제 콜백을 호출해야 할 함수에서 다음과 같이 할당이 되었는지 확인 후 호출 한다.
1: if (null != cbOnTap) cbOnTap.onTap(p, mapView);
간단한 콜백 사용 예제는 다음과 같다.
1: onTapCallback cb = new onTapCallback() {
2:
3: @Override
4: public void onTap(GeoPoint p, MapView mapView) {
5: CenterLocation(p);
6: }
7: };
8:
9: il.setOnTapCallback(cb);
C2DM이란 안드로이드 2.2 프로요 버전 부터 푸시 서버를 제공해 주는 기능이다.
이하 소스는 http://www.androidside.com/bbs/board.php?bo_table=B46&wr_id=14705
에서 소스를 받아 수정 하였다.
푸시서버를 사용하기 위해서는 다음과 같은 절차가 필요하다.
- 구글에 해당 서버 사용 인증 요청
http://code.google.com/intl/ko-KR/android/c2dm/signup.html에서 등록 요청 - 메일로 승인 확인
- 소스 코딩
C2dm_BroadcastReceiver.java
package com.coolsharp.push; import android.content.BroadcastReceiver; public class C2dm_BroadcastReceiver extends BroadcastReceiver { static String registration_id = null; /* 메시지 도착 handleRegistration(context, intent); } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { c2dm_msg = intent.getExtras().getString("msg"); System.out.println("c2dm_msg======>" + c2dm_msg); } /** registration_id = intent.getStringExtra("registration_id"); System.out.println("registration_id====>" + registration_id); if (intent.getStringExtra("error") != null) { Log.v("C2DM_REGISTRATION", ">>>>>" + "Registration failed, should try again later." + "<<<<<"); } else if (intent.getStringExtra("unregistered") != null) { Log.v("C2DM_REGISTRATION", ">>>>>" + "unregistration done, new messages from the authorized sender will be rejected" + "<<<<<"); } else if (registration_id != null) { System.out.println("registration_id complete!!"); } |
Coolsharp_pushActivity.java
package com.coolsharp.push; import java.io.BufferedReader; import android.app.Activity; public class Coolsharp_pushActivity extends Activity { // 아래 문자는 수정하면 안됨. @Override // 마지막 저장된 인증 msg_text = (EditText) findViewById(R.id.msg_text); msg_send.setOnClickListener(new OnClickListener() { /** postDataBuilder.append("registration_id=" + regId); // 등록ID byte[] postData = postDataBuilder.toString().getBytes("UTF8"); URL url = new URL("https://android.apis.google.com/c2dm/send"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); OutputStream out = conn.getOutputStream(); conn.getInputStream(); } /** StringBuffer postDataBuilder = new StringBuffer(); byte[] postData = postDataBuilder.toString().getBytes("UTF8"); URL url = new URL("https://www.google.com/accounts/ClientLogin"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); OutputStream out = conn.getOutputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); String sidLine = br.readLine(); System.out.println("sidLine----------->>>" + sidLine); authtoken = authLine.substring(5, authLine.length()); return authtoken; } |
Util.java
package com.coolsharp.push; import android.content.Context; public class Util { public static boolean setSharedPreferencesValue(Context context, String name, int mode, String key, String value) { |
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <uses-sdk android:minSdkVersion="8" /> <application <category android:name="android.intent.category.LAUNCHER" /> <receiver <category android:name="com.coolsharp.push" /> <category android:name="com.coolsharp.push" /> <permission <uses-permission android:name="com.coolsharp.push.permission.C2D_MESSAGE" /> </manifest> |
안드로이드에서는 구글 맵을 쉽게 연동 할 수 있다.
다음과 같이 프로젝트를 생성한다.
AndroidManifest.xml을 다음과 같이 생성한다.
<?xml version="1.0" encoding="utf-8"?> <uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application <category android:name="android.intent.category.LAUNCHER" /> <uses-library android:name="com.google.android.maps" android:required="true" /> </manifest> |
퍼미션과 라이브러리를 추가한다.
package com.coolsharp; import android.os.Bundle; import com.google.android.maps.MapActivity; public class Coolsharp_googleMAPActivity extends MapActivity { @Override |
MapActivity에서 상속 받게 수정한다.
<?xml version="1.0" encoding="utf-8"?> <TextView </LinearLayout> |
구글 사이트에서 발급받은 키를 추가한다.
빌드하고 설치하면 다음과 같은 화면이 나온다.
ADT 14 이후 자동으로 설치되었던 Google APIs가 항목에 보이지 않는 문제가 발생하였습니다.
1시간 넘게 삽질 중 원인을 찾았습니다.
Tools>Manage Add-on Sites…
New
http://dl-ssl.google.com/android/repository/addon.xml 입력
JDK 1.7 부터 keysotore Fingerprint가 기본 SHA1으로 발급 됨
해당 문제를 해결하기 위해 다음과 같이 명령어를 쓰면 MD5 정보도 볼 수 있음
keytool -list -v -keystore debug.keystore
http://code.google.com/intl/ko-KR/android/maps-api-signup.html
사이트에서 생성된 MD5를 이용하여 API 키를 발급 받으면 된다.
토요일 쉬는날 모처럼 알라딘 중고서점을 다녀왔다.
인터넷 상으로 보았을 때는 좋은 책들이 많이 있을 것을 기대하고 갔는데 막상 들려보니 다음과 같은 문제점이 있었다.
가격이 비싸다.
뭐 흙을 파서 장사를 하는 것이 아닌 것은 이해가 된다.
시내 한 복판에 매장을 운영하니 가게 유지비도 많이 드는 것도 이해가 된다.
하지만 책 상태가 어느 정도 양호하면 거의 회색 스티커(5000원 이상)이 붙어 있다.
또한 할인율 또한 많지가 않다.
막상 사려고 한 책이 정가가12000원 짜리고 판매가가 8000원 정도였는데 인터넷 서점 할인을 받아 새 책으로 구입을 하더라도 별 차이가 없을 듯 싶었다.
물론 책마다 달랐지만 어느 정도 상태가 좋아 보이는 책은 정가의 약 3분의 2정도에서 2분의 1정도의 가격이었다.
컨텐츠 퀄리티가 떨어진다.
좋은 책을 만나기가 쉽지가 않았다.
책 중 자기계발서가 가장 많은 듯 했고 수험서 조금 눈에 띄었다.
전공 분야 책은 찾기는 힘들 듯 하고 한국 소설은 책장 하나 정도 밖에 없었고 외국 소설도 그리 많지는 않았다.
방금 들어온 책 쪽에 사람들이 관심을 많이 보이는 듯 했는데 남들에게 선택 받지 못한 무수히 많은 책장의 책보다 갓 들어온 책 쪽에 퀄리티 있는 책을 찾는 기대감이랄까…
여하튼 좋은 책을 만나면 모래 속의 진주를 만난 듯한 느낌이 들지도 모르겠다.
그런 재미를 느끼기에는 부족함이 없을 듯 하다.
2000원 짜리 책 코너를 보니 거의 보이는 책이라곤 판타지 류 정도였다.
뭐 이 가격에 좋은 책을 산다는 것은 어찌보면 욕심일 듯 하지만 그래도 역시 중고 책이라도 좋은 책은 값을 지불해야 만날 수가 있다.
이 코너 책들은 쓰레기 분리수거를 돈주고 해주는 느낌이랄까…
정리
90분 돌아다니다가 결국 한 권의 책도 사들지 못하고 나왔다.
이럴 바에 그냥 제 값 주고 보고 싶은 책을 사고 싶었다.
허탈한 기분에 종로 2가 지하상가 영풍문고를 찾았다.
보고 싶은 책 몇권을 선정하고 집으로 돌아왔다.
안드로이드 트위터 연동에 대해 다음과 같이 정리를 해 보았다.
참고 사이트
http://easymicro.egloos.com/5480058 http://blog.outsider.ne.kr/434
- 트위터 모듈 추가
- 트위터 인스턴스 생성 twitter = new TwitterFactory().getInstance();
- 트위터 컨슈머 키 대입 twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
- 리퀘스트 토큰 생성 후 인텐스 생성하며 엑티비티 호출 rqToken = twitter.getOAuthRequestToken(CALLBACK_URL.toString());
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(rqToken.getAuthorizationURL()))); - 엑세스 토큰 얻어옴 rqToken = twitter.getOAuthRequestToken(CALLBACK_URL.toString()); protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && CALLBACK_URL.getScheme().equals(uri.getScheme())) {
String oauth_verifier = uri.getQueryParameter("oauth_verifier");
try {
acToken = twitter.getOAuthAccessToken(rqToken, oauth_verifier);
saveData(S_CONSUMER_KEY, acToken.getToken());
saveData(S_CONSUMER_SECRET, acToken.getTokenSecret());
} catch (TwitterException e) {
Log.e("coolsharp", e.getMessage());
}
}
}
PIN 코드를 입력하지 않기 위해서 콜백 URL을 트위터 설정에서 꼭 해야 한다.
URL은 어떤 것이든 관계 없다.
트위터를 연동하기 위해서 http://twitter4j.org/en/index.html 라이브러리를 써야 한다.
twitter4j-android-2.2.6-SNAPSHOT.zip(slimmed version for Android platform)
package com.coolsharp.test;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
import twitter4j.Paging;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
/**
* @author coolsharp
*
*/
public class TwitterApp extends Activity {
/**
* Twitter instance object
*/
private Twitter twitter;
private AccessToken acToken = null;
private RequestToken rqToken = null;
private Status status = null;
private static final String CONSUMER_KEY = "트위터에서 받아온 컨슈머 키";
private static final String CONSUMER_SECRET = "트위터에서 받아온 컨슈머 비밀 키";
private static final String S_CONSUMER_KEY = "CONSUMER_KEY";
private static final String S_CONSUMER_SECRET = "CONSUMER_SECRET";
public static Uri CALLBACK_URL = Uri.parse("wefu://twitter");
/** 생성. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 트위터 인스턴스 얻기
twitter = new TwitterFactory().getInstance();
// 컨슈머 키 대입
twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
// 버튼 리스너 등록
Button btn = (Button) findViewById(R.id.btnAuthorize);
btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
try {
getToken();
if (null != acToken) getList();
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 토큰 발행
}
});
}
/**
* 토큰 얻기
* @return
* @throws TwitterException
*/
private boolean getToken() throws TwitterException {
boolean res = false;
// 토큰이 발행 되지 않았으면
if (null == acToken) {
// 최근 사용한 토큰 읽기
String key = loadData(S_CONSUMER_KEY);
String value = loadData(S_CONSUMER_SECRET);
// 토큰 값이 모두 있으면
if ((!key.equals("")) && (!value.equals(""))) {
// 기존 토큰 값으로 토큰 발행
acToken = new AccessToken(key, value);
twitter.setOAuthAccessToken(acToken);
res = true;
}
else {
// 인증 시작
startAuth();
}
} else {
}
if (null != acToken) {
// 트위터 글쓰기
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
status = twitter.updateStatus("" + dateFormat.format(new Date(System.currentTimeMillis())).toString());
}
return res;
}
/**
* 인증 시작
* @throws TwitterException
*/
private void startAuth() throws TwitterException {
rqToken = twitter.getOAuthRequestToken(CALLBACK_URL.toString());
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(rqToken.getAuthorizationURL())));
}
/**
* @throws TwitterException
*/
private void getList() throws TwitterException {
List<Status> statuses;
Paging page = new Paging();
page.count(20);
page.setPage(1);
statuses = twitter.getHomeTimeline(page);
for (Status status : statuses) {
Log.i("coolsharp", status.getText());
}
}
/**
* 스트링 데이터 저장하기
* @param Key
* @param Data
* @return
*/
private boolean saveData(String Key, String Data) {
SharedPreferences prefs = getSharedPreferences("coolsharp", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(Key, Data);
return editor.commit();
}
/**
* 스트링 데이터 불러오기
* @param Key
* @return
*/
private String loadData(String Key) {
SharedPreferences prefs = getSharedPreferences("coolsharp", MODE_PRIVATE);
return prefs.getString(Key, "");
}
/* (non-Javadoc)
* 인텐트 생성
* @see android.app.Activity#onNewIntent(android.content.Intent)
*/
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && CALLBACK_URL.getScheme().equals(uri.getScheme())) {
String oauth_verifier = uri.getQueryParameter("oauth_verifier");
try {
acToken = twitter.getOAuthAccessToken(rqToken, oauth_verifier);
saveData(S_CONSUMER_KEY, acToken.getToken());
saveData(S_CONSUMER_SECRET, acToken.getTokenSecret());
} catch (TwitterException e) {
Log.e("coolsharp", e.getMessage());
}
}
}
}
트위터는 OAuth를 사용하므로 해당 알고리즘에 대한 용어 정리부터 시작함
An open protocol to allow secure API authorization in a simple and standard method from desktop and web applications. (데스크탑과 웹어플리케이션에서 간단하고 표준적인 방법으로 안전한 API 인증을 하도록 하는 공개 프로토콜입니다.)
- 서비스 프로바이더(Service Provider) – API를 제공하는 서비스 주체(페이스북, 트위터)
- 컨수머(Consumer) – 서비스 프로바이더로 부터 제공된 API를 사용하여 개발된 애플리케이션(내가 만들 어플)
- 사용자(users) – 일반 유저(내가 만든 어플을 사용하는 사람들)
- 보호된 자원(Protected Resources): 서비스 프로바이터의 데이터들(페이스북 대화 내용)
- 컨수머 키(Consumer Key) : 서비스 프로바이더에 접근할 컨수머의 인증키
- 컨수머 시크릿(Consumer Secret) : 컨수머 키의 암호키
-
요청 토큰(Request Token) : 사용자로부터 위임 권한(Authorization)을 얻기 위해 Consumer가 사용하는 토큰, Access Token으로 교환된다.
-
접근 토근(Access Token) : 사용자의 보호된 자원에 접근하기 위해 Consumer가 사용하는 값
대충 정리하자면 다음과 같음
A 서비스의 특정 기능을 B 서비스가 쓰고 싶은데 B 서비스에서 A 서비스를 접근하기 위한 인증
A 서비스는 B 서비스에게 A 서비스 계정 정보를 주지 않고 토큰이라는 것을 발행하여 접근 할 수 있게 함
다음 플로우를 보면 이해가 쉬움
출처 : http://developer.yahoo.com/oauth/guide/oauth-auth-flow.html
페이스북부터 연동하려 하였으나 페이스북 계정이 생성 후 별로 사용치 않은 관계로 앱 등록이 불가능했다.
때문에 우선 트위터로 안드로이드 연동부터 공부 후 페이스북은 추후 연동하도록 하겠다.
트위터 개발자 등록 사이트는 다음과 같다.
Create an app을 클릭한다.
Name, Descriptoion, Web Site를 입력 후 생성한다.
오늘부터 안드로이드에 facebook을 연동하는 방법을 모색한다.
그 과정에 대한 일종의 기록을 다음과 같이 기록한다.
다음 사이트에 안드로이드 튜토리얼이 있다.
http://developers.facebook.com/docs/mobile/android/build/
다음 페이지에 가서 새로운 앱을 등록하자.
허용여부를 묻는 화면에서 허용을 버튼을 클릭하자.
다음과 같은 화면이 나온다.
새 앱 만들기를 클릭하자.
App Display Name은 사용자에게 보여질 어플리케이션 이름이다.
App Namespace는 Open Graphic과 Canvas Page를 위한 개발 어플을 위한 네임스페이스이다.
계속하기를 클릭하면
자동 가입을 막기 위한 문자 입력 단계가 있다.
글을 입력하고 확인을 클릭한다.
가짜 아이디로 오해 받는 듯…
이렇게 좌절… 쩝
우선 테스트 계정으로 라도 연동해 봐야겠다.
public void onEnter()
{
super.onEnter();
// 윈도우 크기 지정
CGSize s = CCDirector.sharedDirector().winSize();
// 5초동안 화면 크기 만큼 이동
CCIntervalAction move = CCMoveBy.action(5, CGPoint.make(s.width,0));
// 서서히 빨라졌다 다시 서서히 느려지는 액션 값을 2.0f로 셋팅
CCIntervalAction move_ease_inout1 = CCEaseInOut.action(move.copy(), 2.0f);
CCIntervalAction move_ease_inout_back1 = move_ease_inout1.reverse();
// 서서히 빨라졌다 다시 서서히 느려지는 액션 값을 3.0f로 셋팅
CCIntervalAction move_ease_inout2 = CCEaseInOut.action(move.copy(), 3.0f);
CCIntervalAction move_ease_inout_back2 = move_ease_inout2.reverse();
// 서서히 빨라졌다 다시 서서히 느려지는 액션 값을 4.0f로 셋팅
CCIntervalAction move_ease_inout3 = CCEaseInOut.action(move.copy(), 4.0f);
CCIntervalAction move_ease_inout_back3 = move_ease_inout3.reverse();
// 0.25f 딜레이
CCIntervalAction delay = CCDelayTime.action(0.25f);
// 액션 대입
CCIntervalAction seq1 = CCSequence.actions(move_ease_inout1, delay, move_ease_inout_back1, delay.copy());
CCIntervalAction seq2 = CCSequence.actions(move_ease_inout2, delay.copy(), move_ease_inout_back2, delay.copy());
CCIntervalAction seq3 = CCSequence.actions(move_ease_inout3, delay.copy(), move_ease_inout_back3, delay.copy());
// 액션 시작
tamara.runAction(CCRepeatForever.action(seq1));
kathia.runAction(CCRepeatForever.action(seq2));
grossini.runAction(CCRepeatForever.action(seq3));
}
public String title() {
return "EaseInOut and rates";
}
}
public void onEnter() {
super.onEnter();
CGSize s = CCDirector.sharedDirector().winSize();
CCIntervalAction move = CCMoveBy.action(3, CGPoint.make(s.width-130,0));
CCIntervalAction move_back = move.reverse();
// 점점 느리게
CCIntervalAction move_ease_in = CCEaseIn.action(move.copy(), 3.0f);
CCIntervalAction move_ease_in_back = move_ease_in.reverse();
// 점점 빠르게
CCIntervalAction move_ease_out = CCEaseOut.action(move.copy(), 3.0f);
CCIntervalAction move_ease_out_back = move_ease_out.reverse();
CCIntervalAction delay = CCDelayTime.action(0.25f);
CCIntervalAction seq1 = CCSequence.actions(move, delay, move_back, delay.copy());
CCIntervalAction seq2 = CCSequence.actions(move_ease_in, delay.copy(), move_ease_in_back, delay.copy());
CCIntervalAction seq3 = CCSequence.actions(move_ease_out, delay.copy(), move_ease_out_back, delay.copy());
CCAction a2 = grossini.runAction(CCRepeatForever.action(seq1));
a2.setTag(1);
CCAction a1 = tamara.runAction(CCRepeatForever.action(seq2));
a1.setTag(1);
CCAction a = kathia.runAction(CCRepeatForever.action(seq3));
a.setTag(1);
schedule("testStopAction", 6.25f);
}
public void testStopAction(float dt) {
unschedule("testStopAction");
tamara.stopAction(1);
kathia.stopAction(1);
grossini.stopAction(1);
}
public String title() {
return "EaseIn - EaseOut - Stop";
}
}
public Test5() {
super();
// 스프라이트 생성
CCSprite sp1 = CCSprite.sprite("grossinis_sister1.png");
CCSprite sp2 = CCSprite.sprite("grossinis_sister2.png");
// 위치 지정
sp1.setPosition(CGPoint.make(100, 160));
sp2.setPosition(CGPoint.make(380, 160));
// 액션 설정
CCIntervalAction rot = CCRotateBy.action(2, 360);
CCIntervalAction rot_back = rot.reverse();
CCAction forever = CCRepeatForever.action(
CCSequence.actions(rot, rot_back));
CCAction forever2 = forever.copy();
forever.setTag(101);
forever2.setTag(102);
addChild(sp1, 0, kTagSprite1);
addChild(sp2, 0, kTagSprite2);
sp1.runAction(forever);
sp2.runAction(forever2);
schedule("addAndRemove", 2.0f);
}
public void addAndRemove(float dt) {
CCNode sp1 = getChildByTag(kTagSprite1);
CCNode sp2 = getChildByTag(kTagSprite2);
// 지우면서 클린업하지 않음
removeChild(sp1, false);
// 지우면서 클린업
removeChild(sp2, true);
// 다시 추가하면 액션이 진행됨
addChild(sp1, 0, kTagSprite1);
// 다시 추가하면 액션이 멈춤
addChild(sp2, 0, kTagSprite2);
}
public String title() {
return "remove and cleanup";
}
public Test4() {
super();
// 스프라이트 생성
CCSprite sp1 = CCSprite.sprite("grossinis_sister1.png");
CCSprite sp2 = CCSprite.sprite("grossinis_sister2.png");
// 위치 지정
sp1.setPosition(CGPoint.make(100, 160));
sp2.setPosition(CGPoint.make(380, 160));
// 차일드 등록
addChild(sp1, 0, 2);
addChild(sp2, 0, 3);
// 2초 있다가 실행
schedule("delay2", 2.0f);
// 4초 있다가 실행
schedule("delay4", 4.0f);
}
public void delay2(float dt) {
// 태그로 차일드 얻어옴
CCNode node = getChildByTag(2);
CCIntervalAction action1 = CCRotateBy.action(1, 360);
node.runAction(action1);
}
public void delay4(float dt) {
unschedule("delay4");
// 태그로 노드 삭제
removeChildByTag(3, false);
}
public String title() {
return "Tags";
}
}