/* * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "define.h" #include "view.h" #include "util/util.h" #define TEXT_LIVETV "Live TV" #define TEXT_FAVORITE "Favorite" #define TEXT_MORE "More" #define NUM_MENU_BTN 4 #define MAX_BTN_COL 4 #define TABLE_PADDING_X 0 #define TABLE_PADDING_Y 0 #define GRID_PADDING 20 enum _object_type { ACTION_DISABLED = -1, ACTION_LIVETV_BTN = 0, ACTION_MORE_BTN, ACTION_FAVORITE_BTN, ACTION_DELETE_BTN, ACTION_FAVORITE_GRID }; enum _content_type { E_CONTENT_MOVIE = 0, E_CONTENT_GALLERY, E_CONTENT_MUSIC, E_CONTENT_MAX }; struct _priv { Evas_Object *win; Evas_Object *base; Evas_Object *scr; Evas_Object *box; Evas_Object *content; Evas_Object *nocontent; Evas_Object *tv_btn; Evas_Object *more_btn; Evas_Object *menu_btn[NUM_MENU_BTN]; Evas_Object *table; Evas_Object *grid; Elm_Object_Item *selected_it; Eina_List *favorite_list; app_media *am; int content_type; }; struct _menu_info { int id; const char *title; const char *style; Eina_Bool disabled; }; struct _content_info { int grid_x; int grid_y; int grid_n; const char *style; int app_contents_type; const char *category; const char *no_content; }; static struct _menu_info menu_info[] = { { .id = ACTION_FAVORITE_BTN, .title = "Favorite", .style = STYLE_BTN_FAVORITE, .disabled = EINA_FALSE }, { .id = ACTION_DELETE_BTN, .title = "Delete", .style = STYLE_BTN_DELETE, .disabled = EINA_TRUE }, { .id = ACTION_DISABLED, .title = NULL, .style = STYLE_BTN_EMPTY, .disabled = EINA_TRUE }, { .id = ACTION_DISABLED, .title = NULL, .style = STYLE_BTN_EMPTY, .disabled = EINA_TRUE } }; struct _content_info content_info[] = { { .grid_x = GRID_ITEM_X_MOVIE, .grid_y = GRID_ITEM_Y_MOVIE, .grid_n = GRID_ITEM_N_MOVIE, .style = STYLE_GRID_MOVIE_ITEM_FAVORITE, .app_contents_type = CONTENTS_MOVIE, .category = CATEGORY_MOVIE, .no_content = "No favorite Movies" }, { .grid_x = GRID_ITEM_X_GALLERY, .grid_y = GRID_ITEM_Y_GALLERY, .grid_n = GRID_ITEM_N_GALLERY, .style = STYLE_GRID_GALLERY_ITEM_FAVORITE, .app_contents_type = CONTENTS_GALLERY, .category = CATEGORY_GALLERY, .no_content = "No favorite Contents" }, { .grid_x = GRID_ITEM_X_MUSIC, .grid_y = GRID_ITEM_Y_MUSIC, .grid_n = GRID_ITEM_N_MUSIC, .style = STYLE_GRID_SONG_ITEM_FAVORITE, .app_contents_type = CONTENTS_MUSIC, .category = CATEGORY_MUSIC, .no_content = "No favorite Songs" } }; static void _hide_done(void *data, Evas_Object *obj, const char *emmision, const char *source) { viewmgr_hide_view(VIEW_ACTION_MENU); } static void _hide_view(struct _priv *priv, bool anim) { if (anim) { elm_object_signal_emit(priv->base, SIG_HIDE_VIEW, SIG_SOURCE_SRC); } else { viewmgr_hide_view(VIEW_ACTION_MENU); } } static bool _livetv_clicked(struct _priv *priv) { if (!util_launch_request(APP_ID_LIVETV, NULL, NULL)) { _ERR("launch failed: %s\n", APP_ID_LIVETV); return false; } _hide_view(priv, false); return true; } static bool _more_clicked(struct _priv *priv) { if (!util_launch_request(APP_ID_FAVORITE, PARAM_CATEGORY, content_info[priv->content_type].category)) { _ERR("launch failed: %s\n", APP_ID_FAVORITE); return false; } _hide_view(priv, false); return true; } static void _update_toast_status(const char *id, enum toast_status status) { struct view_update_data vdata; vdata.id = id; vdata.status = status; viewmgr_update_view(VIEW_BASE, UPDATE_TOAST, &vdata); } static bool _favorite_clicked(struct _priv *priv, Evas_Object *obj) { app_media_info *mi; int r; bool favorite; r = media_content_connect(); if (r != MEDIA_CONTENT_ERROR_NONE) { _ERR("failed to connect to media content"); return false; } mi = app_media_get_info(priv->am); if (!mi) { _ERR("failed to get app media info"); goto err; } r = app_contents_favorite_check(CONTENTS_MEDIA, mi->media_id, &favorite); if (r != APP_CONTENTS_ERROR_NONE) { _ERR("failed to check favorite"); goto err; } r = app_contents_favorite_set(CONTENTS_MEDIA, mi->media_id, !favorite); if (r != APP_CONTENTS_ERROR_NONE) { _ERR("failed to set favorite"); goto err; } app_media_update(priv->am); mi = app_media_get_info(priv->am); if (!mi) { _ERR("failed to get app media info"); goto err; } _update_toast_status(mi->media_id, E_TOAST_SHOW); viewmgr_update_view(VIEW_BASE, UPDATE_CONTENT_ITEM, NULL); _hide_view(priv, true); media_content_disconnect(); return true; err: media_content_disconnect(); return false; } static bool _delete_clicked(struct _priv *priv, Evas_Object *obj) { /* It will be implemented later */ return true; } static void _key_down_cb(int id, void *data, Evas *e, Evas_Object *obj, Evas_Event_Key_Down *ev) { struct _priv *priv; if (!ev || !data) return; priv = data; if (!strcmp(ev->keyname, KEY_BACK) || !strcmp(ev->keyname, KEY_ESC)) _hide_view(priv, true); } static void _mouse_move_cb(int id, void *data, Evas *e, Evas_Object *obj, Evas_Event_Mouse_Move *ev) { Elm_Object_Item *it; if (!obj || !ev) return; switch (id) { case ACTION_LIVETV_BTN: case ACTION_MORE_BTN: case ACTION_FAVORITE_BTN: case ACTION_DELETE_BTN: if (!elm_object_focus_get(obj)) elm_object_focus_set(obj, EINA_TRUE); break; case ACTION_FAVORITE_GRID: it = elm_gengrid_at_xy_item_get(obj, ev->cur.canvas.x, ev->cur.canvas.y, NULL, NULL); if (!it) return; if (!elm_object_item_focus_get(it)) elm_object_item_focus_set(it, EINA_TRUE); break; } } static void _clicked_cb(int id, void *data, Evas_Object *obj) { struct _priv *priv; if (!data || !obj) return; priv = data; switch (id) { case ACTION_LIVETV_BTN: _livetv_clicked(priv); break; case ACTION_MORE_BTN: _more_clicked(priv); break; case ACTION_FAVORITE_BTN: _favorite_clicked(priv, obj); break; case ACTION_DELETE_BTN: _delete_clicked(priv, obj); break; default: break; } } static void _grid_selected(void *data) { app_media *am; struct view_update_data vdata; struct _priv *priv; if (!data) { _ERR("invalid argument"); return; } priv = data; am = elm_object_item_data_get(priv->selected_it); if (!am) { _ERR("failed to get app media"); return; } vdata.list = priv->favorite_list; vdata.index = util_get_media_index(vdata.list, am); vdata.id = VIEW_BASE; _hide_view(priv, false); switch (priv->content_type) { case E_CONTENT_MOVIE: case E_CONTENT_GALLERY: viewmgr_update_view(VIEW_VIEWER, UPDATE_CONTENT, &vdata); viewmgr_push_view(VIEW_VIEWER); break; case E_CONTENT_MUSIC: viewmgr_update_view(VIEW_MPLAYER, UPDATE_CONTENT, &vdata); viewmgr_push_view(VIEW_MPLAYER); break; default: break; } } static void _selected_cb(int id, void *data, Evas_Object *obj, Elm_Object_Item *it) { struct _priv *priv; if (!data || !it) return; priv = data; switch (id) { case ACTION_FAVORITE_GRID: priv->selected_it = it; ecore_job_add(_grid_selected, priv); break; default: break; } } static input_handler _handler = { .key_down = _key_down_cb, .mouse_move = _mouse_move_cb, .clicked = _clicked_cb, .selected = _selected_cb }; static char *_grid_text_get(void *data, Evas_Object *obj, const char *part) { app_media *am; app_media_info *info; char buf[32]; if (!data) return NULL; am = data; info = app_media_get_info(am); if (!info) { _ERR("failed to get media info"); return NULL; } if (!strcmp(part, PART_ELM_TEXT_TITLE)) return strdup(info->title); else if (!strcmp(part, PART_ELM_TEXT_ARTIST)) return strdup(info->audio->artist); else if (!strcmp(part, PART_ELM_TEXT_ALBUM)) return strdup(info->audio->album); else if (!strcmp(part, PART_ELM_TEXT_PLAYTIME)) { if (info->media_type != MEDIA_CONTENT_TYPE_VIDEO) return NULL; util_time_string(buf, sizeof(buf), info->video->duration, false); return strdup(buf); } return NULL; } static Evas_Object *_grid_content_get(void *data, Evas_Object *obj, const char *part) { Evas_Object *content; app_media *am; app_media_info *info; struct color_data bg; if (!data) return NULL; am = data; info = app_media_get_info(am); if (!info) { _ERR("failed to get media info"); return NULL; } content = NULL; if (!strcmp(part, PART_ELM_SWALLOW_THUMBNAIL)) { content = util_add_image(obj, info->thumbnail_path); if (!content) goto err; } else if (!strcmp(part, PART_ELM_SWALLOW_PICTURE)) { if (info->media_type != MEDIA_CONTENT_TYPE_IMAGE) return NULL; if (info->thumbnail_path) return NULL; content = util_add_image(obj, IMAGE_THUMBNAIL_PICTURE); if (!content) goto err; } else if (!strcmp(part, PART_ELM_SWALLOW_VIDEO)) { if (info->media_type != MEDIA_CONTENT_TYPE_VIDEO) return NULL; content = util_add_image(obj, IMAGE_THUMBNAIL_PLAY); if (!content) goto err; } else if (!strcmp(part, PART_ELM_SWALLOW_TEXTBG)) { content = evas_object_rectangle_add(obj); if (!content) goto err; app_contents_get_color(info->title, NULL, &bg); evas_object_color_set(content, bg.r, bg.g, bg.b, bg.a); } if (content) evas_object_show(content); return content; err: _ERR("failed to create evas object"); return NULL; } Elm_Gengrid_Item_Class *_get_grid_item_class(int type) { Elm_Gengrid_Item_Class *ic; ic = elm_gengrid_item_class_new(); if (!ic) { _ERR("failed to create gengrid item class"); return NULL; } ic->func.text_get = _grid_text_get; ic->func.content_get = _grid_content_get; ic->item_style = content_info[type].style; return ic; } static int _get_content_type(app_media_info *info) { int type; type = E_CONTENT_MAX; switch (info->media_type) { case MEDIA_CONTENT_TYPE_IMAGE: type = E_CONTENT_GALLERY; break; case MEDIA_CONTENT_TYPE_VIDEO: if (util_check_movie_type(info->video->copyright)) type = E_CONTENT_MOVIE; else type = E_CONTENT_GALLERY; break; case MEDIA_CONTENT_TYPE_MUSIC: type = E_CONTENT_MUSIC; break; default: break; } return type; } static void _free_favorite_list(Eina_List *list) { app_media *am; EINA_LIST_FREE(list, am) app_media_destroy(am); } static void _favorite_list_foreach(gpointer data, gpointer user_data) { media_info_h media; app_media *am; Eina_List **l; int r; char *id; if (!data || !user_data) { _ERR("invalid argument"); return; } id = (char *)data; l = (Eina_List **)user_data; r = media_info_get_media_from_db(id, &media); if (r != MEDIA_CONTENT_ERROR_NONE) { _ERR("failed to get media handle"); return; } am = app_media_create(media); if (!am) { _ERR("failed to create app media"); media_info_destroy(media); return; } *l = eina_list_append(*l, am); } static Eina_List *_get_favorite_list(int type) { GList *id_list; Eina_List *list; int r; r = media_content_connect(); if (r != MEDIA_CONTENT_ERROR_NONE) { _ERR("failed to connect to media content"); return NULL; } id_list = NULL; if (app_contents_get_favorite_list(type, &id_list) != APP_CONTENTS_ERROR_NONE) { _ERR("failed to get favorite list"); return NULL; } list = NULL; g_list_foreach(id_list, _favorite_list_foreach, &list); app_contents_free_favorite_list(id_list); media_content_disconnect(); return list; } static bool _draw_livetv_btn(struct _priv *priv) { Evas_Object *btn; btn = util_add_button(priv->content, STYLE_BTN_LIVETV, TEXT_LIVETV); if (!btn) return false; inputmgr_add_callback(btn, ACTION_LIVETV_BTN, &_handler, priv); elm_object_part_content_set(priv->content, PART_ACTION_TV_BTN, btn); evas_object_show(btn); elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_LEFT); elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_RIGHT); priv->tv_btn = btn; return true; } static bool _draw_more_btn(struct _priv *priv) { Evas_Object *btn; btn = util_add_button(priv->box, STYLE_BTN_MORE, TEXT_MORE); if (!btn) return false; inputmgr_add_callback(btn, ACTION_MORE_BTN, &_handler, priv); evas_object_show(btn); elm_box_pack_end(priv->box, btn); elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_LEFT); elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_RIGHT); priv->more_btn = btn; return true; } static bool _draw_menu_area(struct _priv *priv) { Evas_Object *table; table = util_add_table(priv->content, TABLE_PADDING_X, TABLE_PADDING_Y); if (!table) { _ERR("failed to add table"); return false; } elm_object_part_content_set(priv->content, PART_ACTION_MENU_AREA, table); priv->table = table; return true; } static bool _draw_favorite_area(struct _priv *priv) { Evas_Object *ly, *grid; elm_object_part_text_set(priv->content, PART_ACTION_FAVORITE_TITLE, TEXT_FAVORITE); ly = elm_layout_add(priv->content); if (!ly) { _ERR("failed to create layout object"); return false; } elm_layout_file_set(ly, EDJEFILE, GRP_ACTION_NO_CONTENT); grid = util_add_gengrid(priv->content, 0, 0, EINA_FALSE); if (!grid) { _ERR("failed to create gengrid object"); evas_object_del(ly); return false; } inputmgr_add_callback(grid, ACTION_FAVORITE_GRID, &_handler, priv); elm_object_focus_region_show_mode_set(grid, ELM_FOCUS_REGION_SHOW_ITEM); elm_object_focus_next_object_set(grid, grid, ELM_FOCUS_LEFT); elm_object_focus_next_object_set(grid, grid, ELM_FOCUS_RIGHT); priv->nocontent = ly; priv->grid = grid; return true; } static void _set_menu_state(Evas_Object *btn, int idx, const char *id) { int r; bool favorite; if (menu_info[idx].disabled) { elm_object_disabled_set(btn, EINA_TRUE); return; } r = app_contents_favorite_check(CONTENTS_MEDIA, id, &favorite); if (r != APP_CONTENTS_ERROR_NONE) { _ERR("failed to check favorite"); return; } if (favorite) elm_object_signal_emit(btn, SIG_ITEM_TOGGLED, SIG_SOURCE_SRC); } static void _update_menu_area(struct _priv *priv) { Evas_Object *btn; app_media_info *mi; int i; int col, row; elm_table_clear(priv->table, EINA_TRUE); mi = app_media_get_info(priv->am); if (!mi) { _ERR("failed to get app media info"); return; } for (i = 0; i < NUM_MENU_BTN; i++) { btn = util_add_button(priv->content, menu_info[i].style, menu_info[i].title); if (!btn) { _ERR("failed to create button object"); return; } _set_menu_state(btn, i, mi->media_id); evas_object_show(btn); inputmgr_add_callback(btn, menu_info[i].id, &_handler, priv); elm_object_focus_next_object_set(btn, priv->grid, ELM_FOCUS_DOWN); col = i % MAX_BTN_COL; row = i / MAX_BTN_COL; elm_table_pack(priv->table, btn, col, row, 1, 1); priv->menu_btn[i] = btn; } evas_object_show(priv->table); elm_object_focus_next_object_set(priv->menu_btn[0], priv->menu_btn[0], ELM_FOCUS_LEFT); elm_object_focus_next_object_set(priv->menu_btn[NUM_MENU_BTN - 1], priv->menu_btn[NUM_MENU_BTN - 1], ELM_FOCUS_RIGHT); } static void _update_no_content_area(struct _priv *priv) { int i; for (i = 0; i < NUM_MENU_BTN; i++) { elm_object_focus_next_object_set(priv->menu_btn[i], priv->more_btn, ELM_FOCUS_DOWN); } elm_object_part_text_set(priv->nocontent, PART_NO_CONTENT_TEXT, content_info[priv->content_type].no_content); elm_object_part_content_set(priv->content, PART_ACTION_NO_CONTENT_BG, priv->nocontent); } static void _update_favorite_area(struct _priv *priv) { Elm_Gengrid_Item_Class *ic; Elm_Object_Item *it; Eina_List *l; app_media *am; int type; int count; elm_gengrid_clear(priv->grid); elm_object_part_content_unset(priv->content, PART_ACTION_NO_CONTENT_BG); elm_object_part_content_unset(priv->content, PART_ACTION_FAVORITE_AREA); type = priv->content_type; if (priv->favorite_list) _free_favorite_list(priv->favorite_list); priv->favorite_list = _get_favorite_list( content_info[type].app_contents_type); if (!priv->favorite_list) { _update_no_content_area(priv); return; } elm_gengrid_item_size_set(priv->grid, ELM_SCALE_SIZE(content_info[type].grid_x), ELM_SCALE_SIZE(content_info[type].grid_y)); ic = _get_grid_item_class(type); EINA_LIST_FOREACH(priv->favorite_list, l, am) { it = elm_gengrid_item_append(priv->grid, ic, am, NULL, NULL); elm_object_item_data_set(it, am); } elm_gengrid_item_class_free(ic); count = eina_list_count(priv->favorite_list) / content_info[type].grid_n; if (eina_list_count(priv->favorite_list) % content_info[type].grid_n) count++; evas_object_size_hint_min_set(priv->grid, 0, ELM_SCALE_SIZE(content_info[type].grid_y * count)); elm_scroller_step_size_set(priv->scr, 0, ELM_SCALE_SIZE(content_info[type].grid_y + GRID_PADDING)); elm_object_part_content_set(priv->content, PART_ACTION_FAVORITE_AREA, priv->grid); } static bool _draw_items(struct _priv *priv) { Evas_Object *base; base = elm_layout_add(priv->box); if (!base) { _ERR("failed to create base object"); return false; } elm_layout_file_set(base, EDJEFILE, GRP_ACTION_MENU_CONTENT); evas_object_size_hint_weight_set(base, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_show(base); elm_box_pack_end(priv->box, base); priv->content = base; if (!_draw_livetv_btn(priv)) { _ERR("failed to draw livetv button"); return false; } if (!_draw_menu_area(priv)) { _ERR("failed to draw menu button"); return false; } if (!_draw_favorite_area(priv)) { _ERR("failed to draw favorite area"); return false; } if (!_draw_more_btn(priv)) { _ERR("failed to draw livetv button"); return false; } return true; } static bool _update_items(struct _priv *priv, struct view_update_data *vdata) { app_media_info *mi; priv->am = eina_list_nth(vdata->list, vdata->index); if (!priv->am) { _ERR("failed to get app media"); return false; } mi = app_media_get_info(priv->am); if (!mi) { _ERR("failed to get app media info"); return false; } priv->content_type = _get_content_type(mi); _update_menu_area(priv); _update_favorite_area(priv); return true; } static Evas_Object *_create(Evas_Object *win, void *data) { struct _priv *priv; Evas_Object *base, *scr, *box; if (!win) { _ERR("failed to get win object"); return NULL; } priv = calloc(1, sizeof(*priv)); if (!priv) { _ERR("failed to allocate priv"); return NULL; } base = elm_layout_add(win); if (!base) { _ERR("failed to create base object"); free(priv); return NULL; } elm_layout_file_set(base, EDJEFILE, GRP_ACTION_MENU_VIEW); evas_object_size_hint_weight_set(base, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(win, base); scr = util_add_scroller(base); if (!scr) { _ERR("failed to add scroller"); evas_object_del(base); free(priv); return NULL; } box = util_add_box(scr, EINA_FALSE); if (!box) { _ERR("failed to add box"); evas_object_del(base); free(priv); return NULL; } evas_object_show(box); elm_object_content_set(scr, box); elm_object_part_content_set(base, PART_ACTION_MENU, scr); priv->win = win; priv->base = base; priv->scr = scr; priv->box = box; viewmgr_set_view_data(VIEW_ACTION_MENU, priv); if (!_draw_items(priv)) { _ERR("failed to draw items"); free(priv); return NULL; } elm_object_signal_callback_add(base, SIG_HIDE_DONE, SIG_SOURCE_EDC, _hide_done, priv); return base; } static void _show(void *view_data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; elm_scroller_region_show(priv->scr, 0, 0, 0, 0); evas_object_show(priv->base); _update_toast_status(NULL, E_TOAST_HIDE); elm_object_focus_set(priv->tv_btn, EINA_TRUE); elm_object_signal_emit(priv->base, SIG_SHOW_VIEW, SIG_SOURCE_SRC); } static void _hide(void *view_data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; elm_object_signal_emit(priv->base, SIG_HIDE_SET, SIG_SOURCE_SRC); evas_object_hide(priv->base); } static void _update(void *view_data, int update_type, void *data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } if (!data) { _ERR("invalid argument"); return; } priv = view_data; switch (update_type) { case UPDATE_CONTENT: _update_items(priv, data); break; default: break; } } static void _pause(void *view_data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; _hide_view(priv, false); } static void _destroy(void *view_data) { struct _priv *priv; int i; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; inputmgr_remove_callback(priv->tv_btn, &_handler); inputmgr_remove_callback(priv->more_btn, &_handler); inputmgr_remove_callback(priv->grid, &_handler); for (i = 0; i < NUM_MENU_BTN; i++) inputmgr_remove_callback(priv->menu_btn[i], &_handler); if (priv->favorite_list) _free_favorite_list(priv->favorite_list); evas_object_del(priv->base); free(priv); } static view_class _vclass = { .view_id = VIEW_ACTION_MENU, .create = _create, .show = _show, .hide = _hide, .update = _update, .pause = _pause, .destroy = _destroy, }; view_class *view_action_menu_get_vclass(void) { return &_vclass; }