학습목표 : network, genlist(generic list)
이번에는 인터넷 웹사이트에서 이미지를 다운받아, 리스트 형태로 보여주는 예를 통해 tizen을 이용해 list를 사용하는 방법, http를 통해 다운로드 하는 방법을 익혀보고자 한다.
아래와 같은 앱을 만들도록 해보자.
네트워크를 통해 이미지를 한개 다운받아보자.
예를들어 http://www.your-webserver.com/images/alfalfa.jpg 라는 이미지 파일을 인터넷에서 http 프로토콜을 통해 다운로드 받아보자. 일반적으로 http 프로토콜로 http 서버에서 이미지를 다운 받는 구현은, socket API를 통해 하는 방법, Lib CURL을 이용하는 방법 등이 있을 수 있다.
다행이도 Tizen은 다운로드 받는 API를 제공하고 있기 때문에 일반적인 네트워크 API를 공부하고, 복잡하게 쓰지 않고서도, 간단하게 다운로드를 받을 수 있다.
include문 하단에 다음과 같은 콜백함수를 정의하자
void cbFileDownloadComp(void *data, const char *file, int status)
{
LOGI("File Download Comp path=%s", file);
}
app_create
부분에서 아래와 같이 download를 걸어주면, Tizen Ecore가 파일을 다운로드하고나서 지정한 callback을 호출해주게 된다.
Ecore_File_Download_Job *job = NULL;
if(ecore_file_download("http://www.your-webserver.com/images/alfalfa.jpg", "/opt/usr/apps/org.tizen.vegetablelist/data/downloaded_img.jpg",
cbFileDownloadComp ,NULL, NULL, &job) == EINA_FALSE)
LOGI("Error, can't start download");
ecore_file_download
함수를 호출했을 때, EINA_FALSE가 리턴되는 경우는 많은 이유가 있을 수 있다.
예를들어, 인터넷이 안켜져있는 상태에서 했을 때나, 아니면, Privilige(Internet에 대한)를 설정하지 않아 API 콜이 거절되었을 때나, 다운받을 PATH의 파일이 이미 로컬에 존재할 때, write권한이 없는 path에 파일을 쓰려고 했을 때 등이 있을 수 있다.
Tizen은 SMACK이라는 보안시스템을 가지고 있는데, 이 SMACK은 권한이 없는 APP이 시스템파일 또는, 다른 앱의 파일을 접근하려고했을 때 이를 막아준다.
SMACK에 대해 간단히 말하자면, 리눅스의 rwx 퍼미션보다 훨씬 더 많은 권한을 자원별로, 설정할 수 있는 진보된 접근 보안 시스템이라 할 수 있겠다.
위에 다운로드 될 경로를 보면 알 수 있듯이, 나의 앱 공간(/opt/usr/apps/org.tizen.vegetablelist/data) 을 지정한 것을 볼 수 있다.
path에서 org.tizen.vegetablelist는 프로젝트 생성시 입력했던 프로젝트 명이다.
data라는 폴더는 앱이 자신의 데이터를 저장하는 용도로, SMACK 규칙에 이미 기본 정의되어 있기 때문에 접근 가능한 폴더이다.
Tizen은 특정 시스템 자원에 대해 접근하려면 Privileges를 tizen-manifest.xml에 설정해야하는 경우가 있는데, 여기서는 http://tizen.org/privilege/internet 이 필요하다.
이제 파일을 성공적으로 다운받았다면 로컬에 해당 파일이 생성되었을 것이다.
해당 파일이 있는지 API를 통해 알아보고싶을 수도 있다. 이 때에는 fopen과 같은 API를 호출해볼 수 도 있겠지만, 타이젠은 전용 API를 제공하므로 그 것을 써보도록 하자.
if(access(pDnFilePath, F_OK) == 0)
LOGI(“File is exist”);
access에 들어갈 수 있는 파라미터는
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
#define X_OK 1 /* Test for execute permission. */
#define F_OK 0 /* Test for existence. */
이 있을 수 있다.
만약 어떤 파일을 삭제하고 싶을 때에는
remove(pDnFilePath);
와 같이 remove API를 이용하도록 하자.
이번에는 list 컨트롤을 사용해보자.
Tizen에서는 내부에 text만을 가지는 간단한 list라는 컨트롤을 가지고 있다.
이 것은 속도는 빠르나, 내부에 어떤 내용을 복잡하게 구성할 수 없다.
여기서는 이미지를 넣어주어야하므로, genlist라는 컨트롤을 사용하도록 하자.
genlist를 만들려면 아래와 같은 코드를 넣어보자
// elm_grid
Evas_Object *elmgrid = elm_grid_add(ad->conform);
elm_grid_size_set(elmgrid, 480, 720);
evas_object_size_hint_weight_set( elmgrid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND );
elm_object_content_set(ad->conform, elmgrid);
evas_object_show(elmgrid);
// elm_genlist
Evas_Object *genlist = elm_genlist_add(elmgrid);
elm_grid_pack(elmgrid, genlist, 0, 120, 480, 600);
evas_object_show(genlist);
genlist를 컨테이너인 grid에 넣고, 이 것을 conform의 content 영역에 set하는 코드이다.
이런 부분은 앞예제들에서 이미 설명되었다.
genlist를 만들기 위해서는 item_class
가 필요한데, item_class
는 genlist에 추가되는 item에 대한 정보를 가진다.
// define itc
Elm_Genlist_Item_Class *itcItem = elm_genlist_item_class_new();
itcItem->item_style = "1line";
itcItem->func.text_get = genlist_text_get_cb;
itcItem->func.content_get = genlist_content_get_cb;
itcItem->func.state_get = NULL;
itcItem->func.del = NULL;
item_class인 itc를 메모리에 동적 할당하고, 멤버들에다가 genlist에 추가되는 item에 대한 스타일이나, 텍스트 제공함수, 컨텐츠 제공함수 등을 설정한다.
여기서 “1line”이라는 스타일은 EDC로 정의된 genlist item용 layout인데, 아래와 같은 part들을 가지고 있다.
elm.icon.left | elm.text.main.left | elm.text.main.right | elm.icon.right |
---|---|---|---|
char* genlist_text_get_cb(void *data, Evas_Object *obj, const char *part)
{
LOGI("requested text part=%s", part);
if (!strcmp(part, "elm.text.main.left"))
{
return strdup(“TEXT IS HERE”);
}
else
return NULL;
}
와 같이 text provider함수에서 “elm.text.main.left”에 대한 text가 요청 되었을 때, 원하는 TEXT를 return해주면, 그 것이 해당 part의 영역에 그려지게 된다.
예를들어, Tizen AppFw은 elm.text.main.left가 필요할 때 part를 “elm.text.main.left”를 인자로 콜백을 불러준다.
이 때 text영역의 왼쪽에 들어갈 텍스트를 리턴하도록 하자.
Tizen AppFw은 elm.text.main.right영역의 text를 그려야할 시점에 part를 “elm.text.main.right”를 인자로 콜백을 불러준다. 이 때 text영역의 오른쪽에 들어갈 텍스트를 리턴하도록 하자.
특정영역에 대해 NULL을 공급하게 되면, 해당 part에 대해서는 공간이 잡히지 않고 그려지지도 않는다.
Evas_Object* genlist_content_get_cb(void *data, Evas_Object *obj, const char *part)
{
...
}
content provider
함수는 text외에 Container 또는 label 또는 이미지 와 같은 text가 아닌것들을 공급할 때 사용한된다.
자, 이제 다운로드 하는 법과 genlist를 만드는 법을 배웠으니 이번에는 두 가지를 엮어, 다운로드가 되기 전에는 텍스트만 나오다가, 다운로드가 되고나서는 genlist item이 update이 되고 다운로드된이미지가 텍스트 옆에 나타나도록하는 실전적인 프로그램을 완성해보자.
include 밑에 콜백 함수들을 정의하자.
void cbFileDownloadComp(void *data, const char *file, int status)
{
LOGI("File Download Comp path=%s", file);
Elm_Object_Item *pItem = data;
LOGI("update pItem = %p", pItem);
elm_genlist_item_fields_update(pItem, "*", ELM_GENLIST_ITEM_FIELD_ALL);
}
char* genlist_text_get_cb(void *data, Evas_Object *obj, const char *part)
{
char* pDnFilePath = data;
char *pFileName = strrchr(pDnFilePath, '/') + 1;
LOGI("requested text part=%s", part);
if (!strcmp(part, "elm.text.main.left"))
{
return strdup(pFileName);
}
else
return NULL;
}
Evas_Object* genlist_content_get_cb(void *data, Evas_Object *obj, const char *part)
{
char* pDnFilePath = data;
LOGI("requested content part=%s", part);
if(!strcmp(part, "elm.icon.right") && access(pDnFilePath, F_OK) == 0)
{
LOGI("access successful pDnFilePath=%s", pDnFilePath);
Evas_Object *content = elm_layout_add(obj);
elm_layout_theme_set(content, "layout", "list/C/type.2", "default");
Evas_Object *iconImg = elm_image_add(content);
elm_image_file_set(iconImg, pDnFilePath, NULL);
elm_image_resizable_set(iconImg, EINA_TRUE, EINA_TRUE);
elm_layout_content_set(content, "elm.swallow.content", iconImg);
return content;
}
else
{
return NULL;
}
}
cbFileDownloadComp
함수에선 다운로드가 완료되면, 업데이트 되어야하는 genlist item에 대해 elm_genlist_item_fields_update
를 호출하도록 한다. 해당 API는 text_get, content_get 모두를 다시 불러 지정한 genlist item을 invalidate하고 다시 그리도록 해준다.
create_base_gui()
함수에서 UI컨트롤들을 만들고 배치하고, 다운로드를 걸도록 하자
static void
create_base_gui(appdata_s *ad)
{
/* Window */
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
elm_win_autodel_set(ad->win, EINA_TRUE);
if (elm_win_wm_rotation_supported_get(ad->win)) {
int rots[4] = { 0, 90, 180, 270 };
elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
}
evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
/* Conformant */
ad->conform = elm_conformant_add(ad->win);
elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE);
evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, ad->conform);
evas_object_show(ad->conform);
// elm_grid
Evas_Object *elmgrid = elm_grid_add(ad->conform);
elm_grid_size_set(elmgrid, 480, 720);
evas_object_size_hint_weight_set( elmgrid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND );
elm_object_content_set(ad->conform, elmgrid);
evas_object_show(elmgrid);
// elm_genlist
Evas_Object *genlist = elm_genlist_add(elmgrid);
elm_grid_pack(elmgrid, genlist, 0, 120, 480, 600);
evas_object_show(genlist);
// define itc
Elm_Genlist_Item_Class *itcItem = elm_genlist_item_class_new();
itcItem->item_style = "1line";
itcItem->func.text_get = genlist_text_get_cb;
itcItem->func.content_get = genlist_content_get_cb;
itcItem->func.state_get = NULL;
itcItem->func.del = NULL;
Elm_Object_Item *pItem = NULL;
char *pDnFilePath = NULL;
char *pUrl = NULL;
Ecore_File_Download_Job *job = NULL;
// append items
pDnFilePath = "/opt/usr/apps/org.tizen.vegetablelist/data/artichoke.jpg";
pUrl = "http://www.your-webserver.com/images/artichoke.jpg";
pItem = elm_genlist_item_append(genlist, itcItem, /*user data*/pDnFilePath, NULL, ELM_GENLIST_ITEM_NONE, genlist_item_selected, NULL);
if(access(pDnFilePath, F_OK) == 0)
remove(pDnFilePath);
if(ecore_file_download(pUrl, pDnFilePath, cbFileDownloadComp ,NULL, /*user data*/pItem, &job) == EINA_FALSE)
LOGI("Error, can't start download");
// append items
pDnFilePath = "/opt/usr/apps/org.tizen.vegetablelist/data/apple.jpg";
pUrl = "http://www.your-webserver.com/images/apple.jpg";
pItem = elm_genlist_item_append(genlist, itcItem, /*user data*/pDnFilePath, NULL, ELM_GENLIST_ITEM_NONE, genlist_item_selected, NULL);
if(access(pDnFilePath, F_OK) == 0)
remove(pDnFilePath);
if(ecore_file_download(pUrl, pDnFilePath, cbFileDownloadComp ,NULL, /*user data*/pItem, &job) == EINA_FALSE)
LOGI("Error, can't start download");
// append items
pDnFilePath = "/opt/usr/apps/org.tizen.vegetablelist/data/almonds.jpg";
pUrl = "http://www.your-webserver.com/images/almonds.jpg";
pItem = elm_genlist_item_append(genlist, itcItem, /*user data*/pDnFilePath, NULL, ELM_GENLIST_ITEM_NONE, genlist_item_selected, NULL);
if(access(pDnFilePath, F_OK) == 0)
remove(pDnFilePath);
if(ecore_file_download(pUrl, pDnFilePath, cbFileDownloadComp ,NULL, /*user data*/pItem, &job) == EINA_FALSE)
LOGI("Error, can't start download");
/* Show window after base gui is set up */
evas_object_show(ad->win);
}
elm_genlist_item_append
API는 지정한 genlist에 item을 추가하는 API인데,
void genlist_item_selected(void *data, Evas_Object *obj, void *event_info)
{
LOGI("genlist_item_selected");
}
와 같은 item_selected handler function pointer
를 지정할 수 있다.
또한, Tizen이 해당 item을 그릴 때 text_get
, content_get
과 같은 callback을 부를때 user data를 넘길 수 있는 필드를 갖고 있다.