action_menu: use viewmgr_hide_view() instead of viewmgr_pop_view()
[profile/tv/apps/native/air_mediahub.git] / src / view / action_menu.c
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <Elementary.h>
18 #include <app_debug.h>
19 #include <app_contents.h>
20 #include <inputmgr.h>
21 #include <viewmgr.h>
22
23 #include "define.h"
24 #include "view.h"
25 #include "util/util.h"
26
27 #define TEXT_LIVETV "Live TV"
28 #define TEXT_FAVORITE "Favorite"
29
30 #define VIDEO_COPYRIGHT "Unknown"
31
32 #define NUM_MENU_BTN 4
33 #define MAX_BTN_COL 4
34
35 #define TABLE_PADDING_X 0
36 #define TABLE_PADDING_Y 0
37
38 enum _object_type {
39         ACTION_LIVETV_BTN = 0,
40         ACTION_MENU_BTN,
41         ACTION_FAVORITE_GRID
42 };
43
44 enum _content_type {
45         E_CONTENT_MOVIE = 0,
46         E_CONTENT_GALLERY,
47         E_CONTENT_MUSIC,
48         E_CONTENT_MAX
49 };
50
51 struct _priv {
52         Evas_Object *win;
53         Evas_Object *base;
54
55         Evas_Object *tv_btn;
56         Evas_Object *menu_btn[NUM_MENU_BTN];
57         Evas_Object *grid;
58
59         Eina_List *favorite_list;
60 };
61
62 struct _menu_info {
63         const char *title;
64         const char *style;
65         Eina_Bool disabled;
66 };
67
68 struct _content_info {
69         int grid_x;
70         int grid_y;
71         const char *style;
72
73         int app_contents_type;
74 };
75
76 static struct _menu_info menu_info[] = {
77         {
78                 .title = "Favorite",
79                 .style = STYLE_BTN_FAVORITE,
80                 .disabled = EINA_FALSE
81         },
82         {
83                 .title = "Delete",
84                 .style = STYLE_BTN_DELETE,
85                 .disabled = EINA_TRUE
86         },
87         {
88                 .title = NULL,
89                 .style = STYLE_BTN_EMPTY,
90                 .disabled = EINA_TRUE
91         },
92         {
93                 .title = NULL,
94                 .style = STYLE_BTN_EMPTY,
95                 .disabled = EINA_TRUE
96         }
97 };
98
99 struct _content_info content_info[] = {
100         {
101                 .grid_x = GRID_ITEM_X_MOVIE,
102                 .grid_y = GRID_ITEM_Y_MOVIE,
103                 .style = STYLE_GRID_MOVIE_ITEM_FAVORITE,
104                 .app_contents_type = CONTENTS_MOVIE
105         },
106         {
107                 .grid_x = GRID_ITEM_X_GALLERY,
108                 .grid_y = GRID_ITEM_Y_GALLERY,
109                 .style = STYLE_GRID_GALLERY_ITEM_FAVORITE,
110                 .app_contents_type = CONTENTS_GALLERY
111         },
112         {
113                 .grid_x = GRID_ITEM_X_MUSIC,
114                 .grid_y = GRID_ITEM_Y_MUSIC,
115                 .style = STYLE_GRID_SONG_ITEM_FAVORITE,
116                 .app_contents_type = CONTENTS_MUSIC
117         }
118 };
119
120 static void _key_down_cb(int id, void *data, Evas *e, Evas_Object *obj,
121                         Evas_Event_Key_Down *ev)
122 {
123         if (!ev)
124                 return;
125
126         if (!strcmp(ev->keyname, KEY_BACK) ||
127                 !strcmp(ev->keyname, KEY_BACK_REMOTE))
128                 viewmgr_hide_view(VIEW_ACTION_MENU);
129 }
130
131 static void _mouse_move_cb(int id, void *data, Evas *e, Evas_Object *obj,
132                         Evas_Event_Mouse_Move *ev)
133 {
134         Elm_Object_Item *it;
135
136         if (!obj || !ev)
137                 return;
138
139         switch (id) {
140         case ACTION_LIVETV_BTN:
141         case ACTION_MENU_BTN:
142                 if (!elm_object_focus_get(obj))
143                         elm_object_focus_set(obj, EINA_TRUE);
144
145                 break;
146         case ACTION_FAVORITE_GRID:
147                 it = elm_gengrid_at_xy_item_get(obj, ev->cur.canvas.x,
148                                         ev->cur.canvas.y, NULL, NULL);
149
150                 if (!it)
151                         return;
152
153                 if (!elm_object_item_focus_get(it))
154                         elm_object_item_focus_set(it, EINA_TRUE);
155
156                 break;
157         }
158 }
159
160 static input_handler _handler = {
161         .key_down = _key_down_cb,
162         .mouse_move = _mouse_move_cb
163 };
164
165 static char *_grid_text_get(void *data, Evas_Object *obj, const char *part)
166 {
167         app_media *am;
168         app_media_info *info;
169         char buf[32];
170
171         if (!data)
172                 return NULL;
173
174         am = data;
175
176         info = app_media_get_info(am);
177         if (!info) {
178                 _ERR("failed to get media info");
179                 return NULL;
180         }
181
182         if (!strcmp(part, PART_ELM_TEXT_TITLE))
183                 return strdup(info->title);
184         else if (!strcmp(part, PART_ELM_TEXT_ARTIST))
185                 return strdup(info->audio->artist);
186         else if (!strcmp(part, PART_ELM_TEXT_ALBUM))
187                 return strdup(info->audio->album);
188         else if (!strcmp(part, PART_ELM_TEXT_PLAYTIME)) {
189                 if (info->media_type != MEDIA_CONTENT_TYPE_VIDEO)
190                         return NULL;
191
192                 util_time_string(buf, sizeof(buf),
193                                         info->video->duration, false);
194                 return strdup(buf);
195         }
196
197         return NULL;
198 }
199
200 static Evas_Object *_grid_content_get(void *data,
201                         Evas_Object *obj, const char *part)
202 {
203         Evas_Object *content;
204         app_media *am;
205         app_media_info *info;
206         struct color_data bg;
207
208         if (!data)
209                 return NULL;
210
211         am = data;
212
213         info = app_media_get_info(am);
214         if (!info) {
215                 _ERR("failed to get media info");
216                 return NULL;
217         }
218
219         content = NULL;
220         if (!strcmp(part, PART_ELM_SWALLOW_THUMBNAIL)) {
221                 content = util_add_image(obj, info->thumbnail_path);
222                 if (!content)
223                         goto err;
224
225                 evas_object_show(content);
226         } else if (!strcmp(part, PART_ELM_SWALLOW_VIDEO)) {
227                 if (info->media_type != MEDIA_CONTENT_TYPE_VIDEO)
228                         return NULL;
229
230                 content = util_add_image(obj, IMAGE_THUMBNAIL_PLAY);
231                 if (!content)
232                         goto err;
233
234                 evas_object_show(content);
235         } else if (!strcmp(part, PART_ELM_SWALLOW_TEXTBG)) {
236                 content = evas_object_rectangle_add(obj);
237                 if (!content)
238                         goto err;
239
240                 app_contents_get_color(info->title, NULL, &bg);
241                 evas_object_color_set(content, bg.r, bg.g, bg.b, bg.a);
242
243                 evas_object_show(content);
244         }
245
246         return content;
247
248 err:
249         _ERR("failed to create evas object");
250         return NULL;
251 }
252
253 Elm_Gengrid_Item_Class *_get_grid_item_class(int type)
254 {
255         Elm_Gengrid_Item_Class *ic;
256
257         ic = elm_gengrid_item_class_new();
258         if (!ic) {
259                 _ERR("failed to create gengrid item class");
260                 return NULL;
261         }
262
263         ic->func.text_get = _grid_text_get;
264         ic->func.content_get = _grid_content_get;
265         ic->item_style = content_info[type].style;
266
267         return ic;
268 }
269
270 static bool _check_movie_type(app_media_info *info)
271 {
272         return strcmp(info->video->copyright, VIDEO_COPYRIGHT);
273 }
274
275 static int _get_content_type(app_media_info *info)
276 {
277         int type;
278
279         type = E_CONTENT_MAX;
280         switch (info->media_type) {
281         case MEDIA_CONTENT_TYPE_IMAGE:
282                 type = E_CONTENT_GALLERY;
283                 break;
284         case MEDIA_CONTENT_TYPE_VIDEO:
285                 if (_check_movie_type(info))
286                         type = E_CONTENT_MOVIE;
287                 else
288                         type = E_CONTENT_GALLERY;
289                 break;
290         case MEDIA_CONTENT_TYPE_MUSIC:
291                 type = E_CONTENT_MUSIC;
292                 break;
293         default:
294                 break;
295         }
296
297         return type;
298 }
299
300 static void _free_favorite_list(Eina_List *list)
301 {
302         app_media *am;
303
304         EINA_LIST_FREE(list, am)
305                 app_media_destroy(am);
306 }
307
308 static void _favorite_list_foreach(gpointer data, gpointer user_data)
309 {
310         media_info_h media;
311         app_media *am;
312         Eina_List **l;
313         int r;
314         char *id;
315
316         if (!data || !user_data) {
317                 _ERR("invalid argument");
318                 return;
319         }
320
321         id = (char *)data;
322         l = (Eina_List **)user_data;
323
324         r = media_info_get_media_from_db(id, &media);
325         if (r != MEDIA_CONTENT_ERROR_NONE) {
326                 _ERR("failed to get media handle");
327                 return;
328         }
329
330         am = app_media_create(media);
331         if (!am) {
332                 _ERR("failed to create app media");
333                 media_info_destroy(media);
334                 return;
335         }
336
337         *l = eina_list_append(*l, am);
338 }
339
340 static Eina_List *_get_favorite_list(int type)
341 {
342         GList *id_list;
343         Eina_List *list;
344         int r;
345
346         r = media_content_connect();
347         if (r != MEDIA_CONTENT_ERROR_NONE) {
348                 _ERR("failed to connect to media content");
349                 return NULL;
350         }
351
352         id_list = NULL;
353         if (app_contents_get_favorite_list(type, &id_list)
354                 != APP_CONTENTS_ERROR_NONE) {
355                 _ERR("failed to get favorite list");
356                 return NULL;
357         }
358
359         list = NULL;
360         g_list_foreach(id_list, _favorite_list_foreach, &list);
361
362         app_contents_free_favorite_list(id_list);
363
364         media_content_disconnect();
365
366         return list;
367 }
368
369 static bool _draw_livetv_btn(struct _priv *priv)
370 {
371         Evas_Object *btn;
372
373         btn = elm_button_add(priv->base);
374         if (!btn) {
375                 _ERR("failed to create button object");
376                 return false;
377         }
378
379         elm_object_style_set(btn, STYLE_BTN_LIVETV);
380         elm_object_text_set(btn, TEXT_LIVETV);
381
382         inputmgr_add_callback(btn, ACTION_LIVETV_BTN, &_handler, priv);
383
384         elm_object_part_content_set(priv->base, PART_ACTION_TV_BTN, btn);
385
386         evas_object_show(btn);
387
388         elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_LEFT);
389         elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_RIGHT);
390
391         priv->tv_btn = btn;
392
393         return true;
394 }
395
396 static bool _draw_menu_area(struct _priv *priv)
397 {
398         Evas_Object *table, *btn;
399         int i;
400         int col, row;
401
402         table = util_add_table(priv->base, TABLE_PADDING_X, TABLE_PADDING_Y);
403         if (!table) {
404                 _ERR("failed to add table");
405                 return false;
406         }
407
408         for (i = 0; i < NUM_MENU_BTN; i++) {
409                 btn = elm_button_add(priv->base);
410                 if (!btn) {
411                         _ERR("failed to create button object");
412                         evas_object_del(table);
413                         return false;
414                 }
415
416                 elm_object_style_set(btn, menu_info[i].style);
417                 elm_object_text_set(btn, menu_info[i].title);
418
419                 if (menu_info[i].disabled)
420                         elm_object_disabled_set(btn, EINA_TRUE);
421
422                 evas_object_show(btn);
423
424                 inputmgr_add_callback(btn, ACTION_MENU_BTN, &_handler, priv);
425
426                 elm_object_focus_next_object_set(btn, priv->grid,
427                                         ELM_FOCUS_DOWN);
428
429                 col = i % MAX_BTN_COL;
430                 row = i / MAX_BTN_COL;
431
432                 elm_table_pack(table, btn, col, row, 1, 1);
433
434                 priv->menu_btn[i] = btn;
435         }
436
437         evas_object_show(table);
438
439         elm_object_focus_next_object_set(priv->menu_btn[0], priv->menu_btn[0],
440                                 ELM_FOCUS_LEFT);
441         elm_object_focus_next_object_set(priv->menu_btn[NUM_MENU_BTN - 1],
442                                 priv->menu_btn[NUM_MENU_BTN - 1],
443                                 ELM_FOCUS_RIGHT);
444
445         elm_object_part_content_set(priv->base, PART_ACTION_MENU_AREA, table);
446
447         return true;
448 }
449
450 static bool _draw_favorite_area(struct _priv *priv)
451 {
452         Evas_Object *grid;
453
454         elm_object_part_text_set(priv->base, PART_ACTION_FAVORITE_TITLE,
455                                 TEXT_FAVORITE);
456
457         grid = util_add_gengrid(priv->base, 0, 0, EINA_FALSE);
458         if (!grid) {
459                 _ERR("failed to create gengrid object");
460                 return false;
461         }
462
463         inputmgr_add_callback(grid, ACTION_FAVORITE_GRID, &_handler, priv);
464
465         elm_object_focus_next_object_set(grid, grid, ELM_FOCUS_LEFT);
466         elm_object_focus_next_object_set(grid, grid, ELM_FOCUS_RIGHT);
467
468         elm_object_part_content_set(priv->base,
469                                 PART_ACTION_FAVORITE_AREA, grid);
470
471         priv->grid = grid;
472
473         return true;
474 }
475
476 static void _update_favorite_area(struct _priv *priv, int type)
477 {
478
479         Elm_Gengrid_Item_Class *ic;
480         Elm_Object_Item *it;
481         Eina_List *favorite_l, *l;
482         app_media *am;
483
484         elm_gengrid_clear(priv->grid);
485
486         elm_gengrid_item_size_set(priv->grid,
487                         elm_config_scale_get() * content_info[type].grid_x,
488                         elm_config_scale_get() * content_info[type].grid_y);
489
490         ic = _get_grid_item_class(type);
491
492         favorite_l = _get_favorite_list(content_info[type].app_contents_type);
493
494         EINA_LIST_FOREACH(favorite_l, l, am) {
495                 it = elm_gengrid_item_append(priv->grid, ic, am, NULL, NULL);
496
497                 elm_object_item_data_set(it, am);
498         }
499
500         elm_gengrid_item_class_free(ic);
501
502         priv->favorite_list = favorite_l;
503 }
504
505 static bool _draw_items(struct _priv *priv)
506 {
507         if (!_draw_livetv_btn(priv)) {
508                 _ERR("failed to draw livetv button");
509                 return false;
510         }
511
512         if (!_draw_favorite_area(priv)) {
513                 _ERR("failed to draw favorite area");
514                 return false;
515         }
516
517         if (!_draw_menu_area(priv)) {
518                 _ERR("failed to draw menu button");
519                 return false;
520         }
521
522         return true;
523 }
524
525 static bool _update_items(struct _priv *priv, struct view_update_data *vdata)
526 {
527         app_media *am;
528         app_media_info *mi;
529         int type;
530
531         am = eina_list_nth(vdata->list, vdata->index);
532         if (!am) {
533                 _ERR("failed to get app media");
534                 return false;
535         }
536
537         mi = app_media_get_info(am);
538         if (!mi) {
539                 _ERR("failed to get app media info");
540                 return false;
541         }
542
543         type = _get_content_type(mi);
544
545         _update_favorite_area(priv, type);
546
547         return true;
548 }
549
550 static Evas_Object *_create(Evas_Object *win, void *data)
551 {
552         struct _priv *priv;
553         Evas_Object *base;
554
555         if (!win) {
556                 _ERR("failed to get win object");
557                 return NULL;
558         }
559
560         priv = calloc(1, sizeof(*priv));
561         if (!priv) {
562                 _ERR("failed to allocate priv");
563                 return NULL;
564         }
565
566         base = elm_layout_add(win);
567         if (!base) {
568                 _ERR("failed to create base object");
569                 free(priv);
570                 return NULL;
571         }
572
573         elm_layout_file_set(base, EDJEFILE, GRP_ACTION_MENU_VIEW);
574
575         evas_object_size_hint_weight_set(base, EVAS_HINT_EXPAND,
576                                 EVAS_HINT_EXPAND);
577         elm_win_resize_object_add(win, base);
578
579         priv->win = win;
580         priv->base = base;
581
582         viewmgr_set_view_data(VIEW_ACTION_MENU, priv);
583
584         if (!_draw_items(priv)) {
585                 _ERR("failed to draw items");
586                 free(priv);
587                 return NULL;
588         }
589
590         return base;
591 }
592
593 static void _show(void *view_data)
594 {
595         struct _priv *priv;
596
597         if (!view_data) {
598                 _ERR("failed to get view data");
599                 return;
600         }
601
602         priv = view_data;
603
604         evas_object_show(priv->base);
605
606         elm_object_focus_set(priv->tv_btn, EINA_TRUE);
607 }
608
609 static void _hide(void *view_data)
610 {
611         struct _priv *priv;
612
613         if (!view_data) {
614                 _ERR("failed to get view data");
615                 return;
616         }
617
618         priv = view_data;
619
620         evas_object_hide(priv->base);
621 }
622
623 static void _update(void *view_data, int update_type, void *data)
624 {
625         struct _priv *priv;
626
627         if (!view_data) {
628                 _ERR("failed to get view data");
629                 return;
630         }
631
632         if (!data) {
633                 _ERR("invalid argument");
634                 return;
635         }
636
637         priv = view_data;
638
639         switch (update_type) {
640         case UPDATE_CONTENT:
641                 _update_items(priv, data);
642                 break;
643         default:
644                 break;
645         }
646 }
647
648 static void _destroy(void *view_data)
649 {
650         struct _priv *priv;
651         int i;
652
653         if (!view_data) {
654                 _ERR("failed to get view data");
655                 return;
656         }
657
658         priv = view_data;
659
660         inputmgr_remove_callback(priv->tv_btn, &_handler);
661         inputmgr_remove_callback(priv->grid, &_handler);
662
663         for (i = 0; i < NUM_MENU_BTN; i++)
664                 inputmgr_remove_callback(priv->menu_btn[i], &_handler);
665
666         if (priv->favorite_list)
667                 _free_favorite_list(priv->favorite_list);
668
669         evas_object_del(priv->base);
670
671         free(priv);
672 }
673
674 static view_class _vclass = {
675         .view_id = VIEW_ACTION_MENU,
676         .create = _create,
677         .show = _show,
678         .hide = _hide,
679         .update = _update,
680         .destroy = _destroy,
681 };
682
683 view_class *view_action_menu_get_vclass(void)
684 {
685         return &_vclass;
686 }