안드로이드 - Android에서 기본 카메라 앱 연동 개발자Tip 안드로이드 이야기2012. 10. 14. 13:59
안드로이드 - Android에서 기본 카메라 앱 연동 개발자Tip
2011.12.28 14:52
NHN 기술전략팀 김상경
Android 기본(Built-in) 카메라 앱으로 사진을 촬영하면, Goggles와 연동해서 사진을 분석하는 기능이 Google Goggles v1.6에 추가(링크)되었습니다. Google이 아닌 Third-Party 개발자도 기본 카메라 앱에서 촬영한 사진을 획득하여 자신의 서비스에 접목시킬 수 있다면 좀 더 사용자 친화적인 서비스를 개발할 수 있지 않을까 하는 생각에서 주목되는 기능입니다.
이 포스팅에서는 Google Goggles v1.6이 어떻게 Android 기본 카메라 앱에서 촬영한 사진을 획득하는지(그림 1의 2번 과정) 분석해 봤습니다.
기본 카메라 앱과 Goggles 연동 과정
다음 그림은 기본 카메라 앱에서 촬영된 사진이 Goggles를 통해 분석되어 사용자에게 전달되는 과정을 나타낸 것이다.
그림 1 기본 카메라 앱과 Goggles v1.6 연동에 의한 사진 분석 과정
기본 카메라 앱과 Goggles v1.6의 연동 순서는 다음과 같다.
- 이때, 분석된 결과가 존재하는 경우에만 NotificationBar로 통지된다.
- NotificationBar로 통지된 결과를 선택하면 사진 분석 결과를 확인할 수 있다.
위와 같은 연동 과정 중 두 번째 항목의 기본 카메라 앱에서 촬영한 사진을 Goggles 앱이 획득하는 방법을 분석해 봤다. 인터넷과 Android DDMS 로그 분석을 사용해 다음과 같은 두 가지 연동 방법을 확인했다.
- com.android.camera.NEW_PICTURE Intent를 이용한 방식
- MediaStore Polling 방식
이 두 가지 연동 방법을 자세히 살펴보자.
연동 방법 1 com.android.camera.NEW_PICTURE Intent
그림 2 com.android.camera.NEW_PICTURE Intent Broadcasting
기본 카메라 앱으로 사진을 촬영하면 다음과 같은 과정을 거친다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 |
public class CameraActivity extends Activity {
public final static String ACTION_NEW_PICTURE = "com.android.camera.NEW_PICTURE" ;
// ... 생략 ...
/**
* com.android.camera.NEW_PICTURE Intent Broadcasting
*
* @param uri MediaStore에 저장된 사진의 URI
*/
private void broadcastIntent(Uri uri) {
Intent intent = new Intent();
intent.setAction(ACTION_NEW_PICTURE);
intent.setData(uri);
sendBroadcast(intent);
}
/**
* MediaStore에 사진 저장하기
*
* @param dirPath 사진을 저장할 Directory 위치
* @param filename 사진 파일 이름
* @param jpegByteArray Jpeg 파일의 ByteArray
* @return MediaStore에 저장된 사진의 URI
*/
private Uri insertMediaStore(String dirPath, String filename, byte [] jpegByteArray) {
String filePath = dirPath + "/" + filename;
try {
ContentValues values = new ContentValues();
values.put(Images.Media.DATE_TAKEN, new Date().getTime());
values.put(Images.Media.ORIENTATION, "0" );
String title = filename.replace( ".jpg" , "" );
values.put(Images.Media.TITLE, title);
values.put(Images.Media.DISPLAY_NAME, filename);
values.put(Images.Media.MIME_TYPE, "image/jpeg" );
values.put(Images.Media.SIZE, jpegByteArray.length);
values.put( "_data" , filePath);
Uri uri = getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream os = getContentResolver().openOutputStream(uri);
os.write(jpegByteArray);
os.close();
Logger.info( "MediaStore Inserted URI:" + uri.toString());
return uri;
} catch (Exception ex) {
Logger.error(ex, "Failed to save the Bitmap file. FilePath: %s" , filePath);
Toast.makeText( this , "Bitmap 저장 실패" , Toast.LENGTH_SHORT).show();
}
return null ;
} } |
코드 1 com.android.camera.NEW_PICTURE Intent Broadcasting 예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
public class IntentListenerService extends Service {
// ... 생략 ...
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Logger.info( "Intent received - " + intent.toString());
}
};
private void startBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction( "com.android.camera.NEW_PICTURE" );
try {
filter.addDataType( "image/*" );
registerReceiver(broadcastReceiver, filter);
} catch (MalformedMimeTypeException ex) {
Logger.error(ex, "BroadcastReceiver registration failure." );
}
}
private void stopBroadcastReceiver() {
unregisterReceiver(broadcastReceiver);
} } |
코드 2 com.android.camera.NEW_PICTURE Intent BroadcastReceiver 예제 코드
기본 카메라 앱이 사용하는 com.android.camera.NEW_PICTURE Intent는 Android SDK에 등록된 표준 API가 아니다(Android SDK Reference에서 찾을 수 없다). 그러나 많은 기본 카메라 앱에서 사진 촬영 후 com.android.camera.NEW_PICTURE Intent를 브로드캐스팅하고 있다. 그러므로 표준 API는 아니지만 Defacto 표준 정도로 생각할 수 있다.
com.android.camera.NEW_PICTURE Intent를 사용하여 연동하는 앱은 Goggles v1.6 외에 Picasa Uploader가 있다.
연동 방법 2 MediaStore Polling
연동 방법 1에서 설명한 com.android.camera.NEW_PICTURE Intent는 Android 표준 API가 아니다. 기본 카메라 앱이 com.android.camera.NEW_PICTURE Intent를 브로드캐스팅할지 여부는 Android 기본 카메라 앱을 만든 제조사가 결정한다.
이를 보완하기 위해 Goggles v1.6은 MediaStore를 1분마다 Polling한다.
그림 3 MediaStore Polling
Goggles는 다음 그림과 같이 백그라운드에서 실행된다.
그림 4 백그라운드 프로세스로 실행되고 있는 Goggles
실제 Android DDMS 로그에 1분마다 기록되는 것을 확인할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
09-26 15:32:51.382: INFO/goggles(910): AuthenticatedService: Allowing authenticated operation for account testphone@nhn.com 09-26 15:32:52.198: INFO/goggles(910): FreshnessHelper: Checking for images newer than 1317018153743 09-26 15:33:52.206: INFO/goggles(910): AuthenticatedService: Allowing authenticated operation for account testphone@nhn.com 09-26 15:33:52.593: INFO/goggles(910): FreshnessHelper: Checking for images newer than 1317018153743 09-26 15:34:52.597: INFO/goggles(910): AuthenticatedService: Allowing authenticated operation for account testphone@nhn.com 09-26 15:34:52.870: INFO/goggles(910): FreshnessHelper: Checking for images newer than 1317018153743 09-26 15:35:52.874: INFO/goggles(910): AuthenticatedService: Allowing authenticated operation for account testphone@nhn.com 09-26 15:35:53.152: INFO/goggles(910): FreshnessHelper: Checking for images newer than 1317018153743 09-26 15:36:53.155: INFO/goggles(910): AuthenticatedService: Allowing authenticated operation for account testphone@nhn.com 09-26 15:36:53.663: INFO/goggles(910): FreshnessHelper: Checking for images newer than 1317018153743 09-26 15:36:53.663: INFO/goggles(910): PictureRequestService: Creating a Picture from Image [imageUri=content://media/external/images/media/1431, description=null, dateTaken=1317018985211, orientation=0] 09-26 15:36:54.105: WARN/goggles(910): TraceTracker: Null action in getTracingCookieForAction. 09-26 15:36:54.112: ERROR/goggles(910): TraceTracker: [REQUEST_TO_RESPONSE]: null TraceAction in beginInterval! 09-26 15:36:57.042: WARN/goggles(910): TraceTracker: TraceAction 0 has already been completed, cannot end interval REQUEST_TO_RESPONSE 09-26 15:36:57.042: WARN/goggles(910): TraceTracker: No request found for action number 0 09-26 15:36:58.378: INFO/goggles(910): QueueingQueryExecutor: Background query succeeded; checking for another query to run. 09-26 15:36:58.452: INFO/goggles(910): NotificationHelper: Launching notification: Notification(vibrate=default,sound=default,defaults=0xffffffff,flags=0x18) |
코드 3 Android DDMS에 기록된 Goggles의 1분 간격 Polling 로그
뿐만 아니라 사진을 찍어 MediaStore를 통해 /mnt/sdcard/DCIM 폴더에 저장하면, Polling 시 사진을 획득해서 Goggles에서 사진 분석을 수행하는 것을 로그로 확인할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 |
public class CameraActivity extends Activity {
private final static String DCIM_PATH = Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera" ;
// ... 생략 ...
/**
* /mnt/sdcard/DCIM 폴더 하위에 Jpeg 파일 저장
*
* @param jpegByteArray Jpeg 파일의 ByteArray
*/
private Uri saveToDCIMFolder( byte [] jpegByteArray) {
String filename = makeFilename();
if (FileUtil.isDirectoryExisted(DCIM_PATH) == false ) {
Toast.makeText( this , DCIM_PATH + "가 존재하지 않습니다." , Toast.LENGTH_SHORT).show();
return null ;
}
return insertMediaStore(DCIM_PATH, filename, jpegByteArray);
}
/**
* MediaStore에 사진 저장하기
*
* @param dirPath 사진을 저장할 Directory 위치
* @param filename 사진 파일 이름
* @param jpegByteArray Jpeg 파일의 ByteArray
* @return MediaStore에 저장된 사진의 URI
*/
private Uri insertMediaStore(String dirPath, String filename, byte [] jpegByteArray) {
String filePath = dirPath + "/" + filename;
try {
ContentValues values = new ContentValues();
values.put(Images.Media.DATE_TAKEN, new Date().getTime());
values.put(Images.Media.ORIENTATION, "0" );
String title = filename.replace( ".jpg" , "" );
values.put(Images.Media.TITLE, title);
values.put(Images.Media.DISPLAY_NAME, filename);
values.put(Images.Media.MIME_TYPE, "image/jpeg" );
values.put(Images.Media.SIZE, jpegByteArray.length);
values.put( "_data" , filePath);
Uri uri = getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream os = getContentResolver().openOutputStream(uri);
os.write(jpegByteArray);
os.close();
Logger.info( "MediaStore Inserted URI:" + uri.toString());
return uri;
} catch (Exception ex) {
Logger.error(ex, "Failed to save the Bitmap file. FilePath: %s" , filePath);
Toast.makeText( this , "Bitmap 저장 실패" , Toast.LENGTH_SHORT).show();
}
return null ;
} } |
코드 4 MediaStore를 통해 /mnt/sdcard/DCIM 하위 폴더에 사진을 저장하는 예제 코드
결론
지금까지 기본 카메라 앱과 Goggles 앱의 연동 방법을 분석해 봤다. 이 연동 방법을 사용해서 기본 카메라 앱에서 촬영한 사진을 활용하는 다양한 앱을 개발할 수 있기를 기대한다.
참고자료
'안드로이드 이야기' 카테고리의 다른 글
안드로이드 - WebsiteMonitoring (0) | 2012.10.15 |
---|---|
안드로이드 - URL을 이용해서 이미지 다운로드 하기 (멀티 쓰레드 이용) (0) | 2012.10.15 |
안드로이드 - Bluetooth Chat 소스 다운로드 (0) | 2012.09.18 |
안드로이드 - 블루투스 제어기 제작하기 (0) | 2012.09.18 |
안드로이드 - 블루투스 BlueTooth (0) | 2012.09.18 |