이 글은 안드로이드 레퍼런스 폰의 팩토리 이미지를 빌드하는 방법에 관하여 간단히 설명해 놓은것입니다.


솔직히 설명할 필요도 없이 아래 세 개의 링크만 클릭하면 모든 설명들이 자세하게 되어 있지만, 약간의 예외 및 기타 잡다한 참견들을 추가하도록 하겠습니다. ^^;


https://source.android.com/setup/build/initializing

https://source.android.com/setup/build/downloading

https://source.android.com/setup/build/building



참고로 위 세 개의 링크만 잘 읽어보면, 쉽게 빌드할 수 있지만, 알고보면, 그렇게 쉽지 않은 이유는 조그마한 환경변수나 버전이 하나만 틀어져버려도 에러가 발생할 수 있기 때문입니다.

그래서, 필자도 처음에는 Mac OS 에서 빌드를 시작해서 결국, Mac OS 위 VmWare Fusion 30일용 + Ubuntu 14.04  에서 몇 번 실패한 끝에 겨우 빌드에 성공했습니다.


일단, Ubuntu 14.04 를 설치한 황을 가정하고 설명을 시작하겠습니다. 이유는 제가 빌드에 성공한 버전이기도 하지만, 그나마 예외적인 사항들이 적은 편이기 때문입니다.


https://source.android.com/setup/build/requirements 를 보면 먼저 간단한 요구사항들이 있습니다.


JDK

See Installing the JDK for the prebuilt path and installation instructions for older versions.



그 중 가장 중요한 것은 어떤 버전을 빌드할 것인가 입니다. 이를 테면, Lollipop 을 빌드한다고 하면,  Java 1.7 이 필요합니다. 최신의 Nougat 이상을 빌드하고 싶다면, 현재 보통 사용하고 있는 1.8 을 그냥 사용 하면 되겠지만, 그 이전 버전일 경우에는 위 표를 반드시 알고 미리 준비해두어야 합니다. 만약, JAVA 버전이 다를 경우, 빌드시 에러가 발생합니다.



이후에는 https://source.android.com/setup/build/initializing 에 따라 14.04 에 해당하는 명령을 실행해주면 됩니다. 위 그림에서 '클릭하여 복사' 하여 실행하셔도 됩니다. 



이젠 소스를 다운로드 받아야 합니다. https://source.android.com/setup/build/downloading 에 따라 소스를 받으면 되는데, repo 설정과 그외 부분은 위 그림에서와 같이 '클릭하여 복사'를 잘 이용하시면 편리합니다. 가장 중요한 것은 어떤 브랜치를 가져올것인가 입니다. 

아래 그림에서 'repo init - u https://android.googesource.com/platform/manifest -b android-4.0.1_r1' 에서 android-4.0.1_r1 에 해당하는 부분인데요.

푸른 색 글씨의 Source Code Tags and Builds 링크를 따라가서 무엇을 받을지를 결정하셔야 합니다.




저는 Nexus6P 를 빌드해야 했기 때문에 Nexus6P 를 검색해서 실제 Factory Image 가 올라와있는 버전(MDB08K)을 선택했습니다.




https://developers.google.com/android/images 에서 미리 자신이 가지고 있는 단말의 Factory Image 를 미리 받아서 언제든 원복 시킬수 있거나 소스코드를 수정했을 때, 본래 이미지와 비교할 수 있는 버전을 받아 놓는게 좋습니다. 팩토리 이미지를 설치하는 방법은 다음 글에서 설명해드릴게요.



그래서 저는 'repo init - u https://android.googesource.com/platform/manifest -b android-6.0.0_r24' 로 다운로드를 받았습니다.

그리고, 페이지의 설명대로 'repo sync' 를 시작하면 다운로드를 시작합니다.

주의하실 점은 소스 자체의 크기는 그렇게 크다고 볼 수 없지만, 요즘은 git 정보가 너무 많아서 다운로드에만 50G 이상이 사용됩니다. 빌드할 때 여유공간까지 생각하면 150G 정도의 충분한 여유공간을 만들어 놓는 것이 좋습니다. 

물론, 정보를 적게 가져오는 방법이 있기는 하지만, 여기서는 일단 스킵하고 최대한 많은 여유공간을 확보해두시는 걸 권장해드리고 싶습니다.

그리고, 다운로드 받는데도 꽤 오랜 시간이 걸립니다. 족히 한시간은 걸렸던 걸로 기억이되네요.


다운로드를 받고나서 빌드를 하는 과정은 오히려 어렵지 않습니다. https://source.android.com/setup/build/building 에 나온 대로 'make clobber' -> 'source build/envsetup.sh' -> 'lunch' -> 'make -j4' 순서로 실행해 주시면 됩니다.



사이트에서는 'lunch aosp_arm-eng' 를 예로 들었는데, 'lunch' 만 실행해 보시면 아래와 같이 옵션이 많이 있다는걸 알게됩니다.



저는 Nexus6P 의 코드네임 angler 를 따라 17번  aosp_angler-userdebug 를 선택해서 빌드했습니다.



BUILD_ID=MDB08K 를 보내 뭔가 제대로 된 느낌입니다. ^^; 

'make -j4' 로 빌드를 시작하면 됩니다. 빌드 환경에 따라 -j2 혹은 -j8 로 시작하셔도 됩니다. 숫자가 클수록 빨리 끝나기는 합니다. 그래도 최소 한시간정도는 기다려야 합니다.

빌드가 성공적으로 끝나면 /WORKING_DIRECTORY/out/target/product/[target_code_name] 에 이미지가 생성됩니다.

저는 Nexus6P 이기 때문에 angler 디렉토리에 생성되어 있습니다.



이렇게 빌드된 이미지 파일들을 단말에 설치하면 됩니다. 설치 방법은 다음글에서 안내해 드릴게요.

Compile 은 안드로이드 상위 버전(예: sdkVersion=19)으로 한 뒤, 하위 버전(예: sdkVersion=8)에서 실행할 때 아래와 같은 에러를 만나게 되는 경우에는 Library Project 혹은 Library 의 문제가 아니라, 프로젝트에서 사용하고 있는 
"?android:attr/" 의 문제이다. 
상위 버전의 attr 을 resource id 를 사용하는 것이기 때문에 하위버전에서는 엉뚱한 리소스를 찾다가 실패하는 것이다. 

 Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x10102fd a=-1} 
혹은 
 java.io.FileNotFoundException: res/drawable-hdpi/divider_horizontal_bright_opaque.9.png

와 같은 에러를 만나게 된다.


프로젝트 xml 파일에서 "?android:attr/" 로 검색하여 최신 속성을 사용하고 있는 것인지 확인해야 한다.


p.s. 본디, 이런 포스팅은 하지 않는데,.. 너무 어이없이 삽질을 하다보니 일단 기록을 남겨둔다.

  1. Denial 2014.08.09 07:01 신고

    저두 정말 어처구니 없는 삽질을 했었네요 ㅠ 눈치챌 즈음에 여기를 발견했습니다. ㅎㅎ

Uri.Builder 사용하시는 분들이 별로 없으신 것 같아서 간단히 설명드립니다.


Uri = scheme://authority/path?query=value 


와 같은 형태입니다.


Uri.Builder 를 사용하시면 Uri 를 쉽게 만들어서 사용하실 수 있습니다.


예를 들어, 


https://gdata.youtube.com/feeds/api/users/abc/playlists?v=2&alt=jsonc


와 같은 URL 이 있다면 아래 코드와 같이 코딩하신 뒤, toString() 을 사용하시면 됩니다.


https://gdata.youtube.com/feeds/api/users/abc/playlists?v=2&alt=jsonc 


Uri.Builder ub=new Uri.Builder();

ub.scheme("https")

.authority("gdata.youtube.com")

.appendPath("feeds").appendPath("api").appendPath("users")

.appendPath("abc").appendPath("playlists")

.appendQueryParameter("v", "2").appendQueryParameter("alt""jsonc");


String url=ub.build().toString();


특히, QueryParameter 의 경우에는 자주 바뀌거나 추가될 수 있기 때문에 코드의 재사용성이 꽤 괜찮을 것 같습니다. 


Google Support Library v7 appcompat 사용 방법

1. Eclipse> File> Import> Existing Android Code Into Workspace


2. [Android SDK Directory]/extras/android/support/ 선택 

- 이렇게 선택할 경우 support 되는 프로젝트가 모두 표시된다. 이 중 하나를 선택한다.




3. 이렇게 추가된 프로젝트는 라이브러리 프로젝트로 설정되어 있다.

- Project> Properties> isLibrary 가 체크되어 있다.


4. 그러므로, v4 와 같이 jar 파일만 lib 디렉토리에 추가해주는 것이 아니라, Project> Properties> Library > Add> 를 통해 라이브러리 프로젝트를 추가해 주어야 한다.


이렇게 jar 파일로만 해결이 되지 않는 이유는 리소스를 포함하고 있기 때문이다.

startActivityForResult() 로 호출한 결과를 어디에서 처리해야 할까? 개인적인 생각으로는 Fragment 에서 시작한 Activity 라면 Fragment 에서 처리하는 것이 맞다고 생각한다.

어쨋건 Fragment 가 생긴 뒤, Activity 에서 시작한 startActivityForResult 의 결과물에 대하여, 시스템에서 Fragment 에서 생성한 경우, 16bit - 2byte shift 해서 처리하고 있다.

즉, 안드로이드 시스템에서 Activity 와 Fragment 에서 시작한 Activity 를 구분하고 있다고 보면 된다. 그래서, 만약, Fragment 에서 생성한 ActivityResult 를 상위 Activity 에서 처리하고 싶다고 한다면, >>16 을 사용해야 한다.


FragmentA.java

----------------------------------- 

.... 

requestCode=1; 

startActivityForResult(intent, requestCode) 

.... 

----------------------------------- 


 ActivityA.java 

----------------------------------- 

.... 

@Override 

protected void onActivityResult ( int requestCode, int resultCode, Intent data) { 

int fragmentRequestCode = requestCode >> 16 ; 

// fragmentRequestCode=1 } 

.... 

----------------------------------- 


여기서 알수 있는 것은 Fragment 에서 생성한 requestCode 는 0xffff 보다 작아야 한다는 것이다. 그래야 본래의 값을 잃어버리지 않을 수 있다.


1. 아침에 95.9 손석희의 시선집중을 들으며 출근했다. 왜 국판은 DMB 를 버리고 FM Radio 를 넣은 모델을 출시를 안하는건가...
2. Page Buddy> Earphone pages 기능은 꽤 유용한 것 같다.

P.S. 근데, 스크린 캡쳐 기능은 없는건가?





안드로이드 2.1 에서 제공하는 기본 아이콘입니다.

출처

Java 파일에서 사용방법 : 
 view.setIcon(android.R.drawable.alert_dark_frame);

xml 파일에서 사용방법 :
 android:icon="@android:drawable/alert_dark_frame"


alert_dark_frame
alert_dark_frame
alert_light_frame
alert_light_frame
arrow_down_float
arrow_down_float
arrow_up_float
arrow_up_float
bottom_bar
bottom_bar
btn_default
btn_default
btn_default_small
btn_default_small
btn_dialog
btn_dialog
btn_dropdown
btn_dropdown
btn_minus
btn_minus
btn_plus
btn_plus
btn_radio
btn_radio
btn_star
btn_star
btn_star_big_off
btn_star_big_off
btn_star_big_on
btn_star_big_on
button_onoff_indicator_off
button_onoff_indicator_off
button_onoff_indicator_on
button_onoff_indicator_on
checkbox_off_background
checkbox_off_background
checkbox_on_background
checkbox_on_background
dark_header
dark_header
dialog_frame
dialog_frame
divider_horizontal_bright
divider_horizontal_bright
divider_horizontal_dark
divider_horizontal_dark
divider_horizontal_dim_dark
divider_horizontal_dim_dark
divider_horizontal_textfield
divider_horizontal_textfield
edit_text
edit_text
editbox_background
editbox_background
editbox_background_normal
editbox_background_normal
editbox_dropdown_dark_frame
editbox_dropdown_dark_frame
editbox_dropdown_light_frame
editbox_dropdown_light_frame
gallery_thumb
gallery_thumb
ic_btn_speak_now
ic_btn_speak_now
ic_delete
ic_delete
ic_dialog_alert
ic_dialog_alert
ic_dialog_dialer
ic_dialog_dialer
ic_dialog_email
ic_dialog_email
ic_dialog_info
ic_dialog_info
ic_dialog_map
ic_dialog_map
ic_input_add
ic_input_add
ic_input_delete
ic_input_delete
ic_input_get
ic_input_get
ic_lock_idle_alarm
ic_lock_idle_alarm
ic_lock_idle_charging
ic_lock_idle_charging
ic_lock_idle_lock
ic_lock_idle_lock
ic_lock_idle_low_battery
ic_lock_idle_low_battery
ic_lock_lock
ic_lock_lock
ic_lock_power_off
ic_lock_power_off
ic_lock_silent_mode
ic_lock_silent_mode
ic_lock_silent_mode_off
ic_lock_silent_mode_off
ic_media_ff
ic_media_ff
ic_media_next
ic_media_next
ic_media_pause
ic_media_pause
ic_media_play
ic_media_play
ic_media_previous
ic_media_previous
ic_media_rew
ic_media_rew
ic_menu_add
ic_menu_add
ic_menu_agenda
ic_menu_agenda
ic_menu_always_landscape_portrait
ic_menu_always_landscape_portrait
ic_menu_call
ic_menu_call
ic_menu_camera
ic_menu_camera
ic_menu_close_clear_cancel
ic_menu_close_clear_cancel
ic_menu_compass
ic_menu_compass
ic_menu_crop
ic_menu_crop
ic_menu_day
ic_menu_day
ic_menu_delete
ic_menu_delete
ic_menu_directions
ic_menu_directions
ic_menu_edit
ic_menu_edit
ic_menu_gallery
ic_menu_gallery
ic_menu_help
ic_menu_help
ic_menu_info_details
ic_menu_info_details
ic_menu_manage
ic_menu_manage
ic_menu_mapmode
ic_menu_mapmode
ic_menu_month
ic_menu_month
ic_menu_more
ic_menu_more
ic_menu_my_calendar
ic_menu_my_calendar
ic_menu_mylocation
ic_menu_mylocation
ic_menu_myplaces
ic_menu_myplaces
ic_menu_preferences
ic_menu_preferences
ic_menu_recent_history
ic_menu_recent_history
ic_menu_report_image
ic_menu_report_image
ic_menu_revert
ic_menu_revert
ic_menu_rotate
ic_menu_rotate
ic_menu_save
ic_menu_save
ic_menu_search
ic_menu_search
ic_menu_send
ic_menu_send
ic_menu_set_as
ic_menu_set_as
ic_menu_share
ic_menu_share
ic_menu_slideshow
ic_menu_slideshow
ic_menu_sort_alphabetically
ic_menu_sort_alphabetically
ic_menu_sort_by_size
ic_menu_sort_by_size
ic_menu_today
ic_menu_today
ic_menu_upload
ic_menu_upload
ic_menu_upload_you_tube
ic_menu_upload_you_tube
ic_menu_view
ic_menu_view
ic_menu_week
ic_menu_week
ic_menu_zoom
ic_menu_zoom
ic_notification_clear_all
ic_notification_clear_all
ic_notification_overlay
ic_notification_overlay
ic_partial_secure
ic_partial_secure
ic_popup_disk_full
ic_popup_disk_full
ic_popup_reminder
ic_popup_reminder
ic_popup_sync
ic_popup_sync
ic_search_category_default
ic_search_category_default
ic_secure
ic_secure
menu_frame
menu_frame
menu_full_frame
menu_full_frame
picture_frame
picture_frame
presence_away
presence_away
presence_busy
presence_busy
presence_invisible
presence_invisible
presence_offline
presence_offline
presence_online
presence_online
progress_indeterminate_horizontal
progress_indeterminate_horizontal
radiobutton_off_background
radiobutton_off_background
radiobutton_on_background
radiobutton_on_background
spinner_background
spinner_background
spinner_dropdown_background
spinner_dropdown_background
star_big_off
star_big_off
star_big_on
star_big_on
star_off
star_off
star_on
star_on
stat_notify_call_mute
stat_notify_call_mute
stat_notify_chat
stat_notify_chat
stat_notify_error
stat_notify_error
stat_notify_missed_call
stat_notify_missed_call
stat_notify_more
stat_notify_more
stat_notify_sdcard
stat_notify_sdcard
stat_notify_sdcard_prepare
stat_notify_sdcard_prepare
stat_notify_sdcard_usb
stat_notify_sdcard_usb
stat_notify_sync
stat_notify_sync
stat_notify_sync_noanim
stat_notify_sync_noanim
stat_notify_voicemail
stat_notify_voicemail
stat_sys_data_bluetooth
stat_sys_data_bluetooth
stat_sys_download
stat_sys_download
stat_sys_download_done
stat_sys_download_done
stat_sys_headset
stat_sys_headset
stat_sys_phone_call
stat_sys_phone_call
stat_sys_phone_call_forward
stat_sys_phone_call_forward
stat_sys_phone_call_on_hold
stat_sys_phone_call_on_hold
stat_sys_speakerphone
stat_sys_speakerphone
stat_sys_upload
stat_sys_upload
stat_sys_upload_done
stat_sys_upload_done
stat_sys_vp_phone_call
stat_sys_vp_phone_call
stat_sys_vp_phone_call_on_hold
stat_sys_vp_phone_call_on_hold
stat_sys_warning
stat_sys_warning
status_bar_item_app_background
status_bar_item_app_background
status_bar_item_background
status_bar_item_background
sym_action_call
sym_action_call
sym_action_chat
sym_action_chat
sym_action_email
sym_action_email
sym_call_incoming
sym_call_incoming
sym_call_missed
sym_call_missed
sym_call_outgoing
sym_call_outgoing
sym_contact_card
sym_contact_card
sym_def_app_icon
sym_def_app_icon
title_bar
title_bar
title_bar_tall
title_bar_tall
toast_frame
toast_frame
zoom_plate
zoom_plate


저희 부부가 사용하는 안드로이드 폰에서 구글 캘린더 사용법을 알려드리고자 글을 씁니다.
우선 안드로이드 폰을 사용하시는 분들은 보통 Google 계정 하나씩은 모두들 만들어서 사용하실 텐데요.
기본적으로 Google 에서 사용하는 캘린더(일정)는 여러 사람이 공유하여 사용할 수 있습니다.


1. 일정을 공유하는 방법 : 캘린더 설정> 이 캘린더 공유하기> 



위 화면에서 "사용자"에 이메일 주소를 입력하시고, "사용권한 설정"을 하신다음에 사용자 추가를 하시면 됩니다.
보통 "사용자 권한 설정"은 " 모든 일정 세부정보 보기" 로 해 놓습니다.
"사용자 권한 설정" 에서 "일정 변경" 혹은 "일정 변경 및 공유 관리" 를 선택하실 수도 있긴 하지만, 
자신의 일정에 대해서는 보통 "읽기"만 가능하게 하시려면, 위와 같이 "모든 일정 세부정보 보기" 정도로 선택하세요.

2. 이렇게 자신의 캘린더를 공유하신 뒤, 공유하는 사람의 계정에서 공유된 캘린더가 보이도록 설정해야 합니다.

맨 처음 캘린더 화면으로 돌아와서 친구의 캘린더 추가하기에 공유한 메일 주소를 추가해 줍니다.
그러면, 아래 그림과 같이 추가된 캘린더들이 각각 다른 색깔로 표시됩니다.

3. 중요한 건 지금 부터,... 이제 안드로이드 폰에서 사용하시면 됩니다.
전 갤럭시S를 사용하고 있는데, 갤럭시S 에서 제공하는 캘린더(일정) 보다는 Jorte 라는 App을 주로 사용하는데요.
이유는 위젯이 비교적 깔끔하고, 보기 편해서입니다.
그리고, 구글 캘린더에서 아직 음력을 제공하지 않는데 반해, Jorte 는 음력 날짜를 보여줄 수 있습니다.
개인적으로 음력 생일을 매년 Repeat 하는 기능이 있었으면 하는데 아직 그런 기능이 나온 캘린더는 없는 것 같습니다.
(있으면 알려주세요.. ^^;)



4. 이렇게 설정을 해 놓으시면, 내가 입력한 일정은 아내에게 보여지고, 아내가 입력한 일정은 저에게도 동일하게 보여집니다.
제가 안드로이드 폰에서 일정을 추가하면, 서버와 동기화 되면서, 아내의 안드로이드 폰에서도 제 일정이 표시되게 됩니다.
일일이 전화해서 이번 주말에 시간이 비는지 아닌지 확인하지 않아도,.. 쉽게 서로의 일정을 확인할 수 있지요.
갑자기 생겨버린 회식이나 비정기적인 주말근무 혹은 주변 인척들의 경조사를 비교적 쉽게 확인할 수 있습니다.
저희처럼 맞벌이 부부이신 분들은 유용하게 사용하실 수 있으리라 생각됩니다.



+ Recent posts

티스토리 툴바