Fix focus error after context view is closed
[profile/tv/apps/native/musicplayer.git] / src / views / song-layout.cpp
1 /*
2  * Copyright (c) 2014 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 <Eina.h>
19 #include "dbg.h"
20 #include "i18n.h"
21
22 #include "AppCommon.h"
23 #include "InputHandler.h"
24 #include <SortMgr.h>
25
26 #include "define.h"
27 #include "common.h"
28 #include "song_info.h"
29 #include "album_info.h"
30 #include "music-controller.h"
31 #include "LayoutMgr.h"
32 #include "common-ui.h"
33 #include "BaseView.h"
34 #include "ExtBaseLayout.h"
35 #include "ViewMgr.h"
36 #include "song-layout.h"
37 #include "base-view.h"
38 #include "Info.h"
39
40
41 enum EObjectType {
42         SONG_LAYOUT,
43         SONG_LAYOUT_GENLIST,
44 };
45
46 struct SSongItemInfo {
47         CSongInfo *sinfo;
48         Elm_Object_Item *item;
49 };
50
51 struct SSongLayout {
52         Evas_Object *win;
53         Evas_Object *base;
54         Evas_Object *songlist;
55         Elm_Object_Item *focused_item;
56         Eina_List *slist;
57         Eina_List *it_infolist;
58         Elm_Genlist_Item_Class *item_class;
59         CMusicController *mhandle;
60         CLayoutMgr* mgr;
61         CViewMgr* vmgr;
62         SContentInfo *ctxtinfo;
63         char *uri;
64
65         SSongLayout() {
66                 memset(this, 0, sizeof(SSongLayout));
67         }
68
69         ~SSongLayout() {
70         }
71 };
72
73
74 char *CSongLayout::sm_CbGetListItemText(void *data, Evas_Object *obj, const char *part)
75 {
76         SSongItemInfo *itinfo = (SSongItemInfo *)data;
77         CSongInfo *sinfo = NULL;
78         char buf[MAX_LENGTH];
79         char *timestr = NULL;
80         int duration;
81         int index;
82
83         if (!itinfo || !obj)
84                 return NULL;
85
86         sinfo = itinfo->sinfo;
87
88         snprintf(buf, sizeof(buf), "%s", MUSIC_STR_EMPTY);
89
90         if (!strcmp(part, "elm.text")) {
91                 index = elm_genlist_item_index_get(itinfo->item);
92                 if ((index & 1) == 1) {
93                         elm_object_item_signal_emit(itinfo->item,
94                                 MUSIC_SIGNAL_EVEN_ROW, MUSIC_BASE_VIEW);
95                 }
96                 else {
97                         elm_object_item_signal_emit(itinfo->item,
98                                 MUSIC_SIGNAL_ODD_ROW, MUSIC_BASE_VIEW);
99                 }
100                 snprintf(buf, sizeof(buf), " %s", sinfo->Name());
101         }
102         else if (!strcmp(part, "elm.text1"))
103                 snprintf(buf, sizeof(buf), "%s", sinfo->Artist());
104         else if (!strcmp(part, "elm.text2"))
105                 snprintf(buf, sizeof(buf), "%s", sinfo->Album());
106         else if (!strcmp(part, "elm.text3")) {
107                 duration = sinfo->Duration();
108                 timestr = CCommonUI::TimeStrFromMilSeconds(duration);
109                 if (timestr) {
110                         snprintf(buf, sizeof(buf), "%s", timestr);
111                         free(timestr);
112                 }
113         }
114
115         if (strcmp(buf, MUSIC_STR_EMPTY))
116                 return strdup(buf);
117
118         return NULL;
119 }
120
121
122 Evas_Object *CSongLayout::sm_CbGetListItemContent(void *data, Evas_Object *obj, const char *part)
123 {
124         SSongItemInfo *itinfo = (SSongItemInfo *)data;
125         CSongInfo *sinfo = NULL;
126         Evas_Object *img = NULL;
127         char *path = NULL;
128         char buf[MAX_LENGTH];
129
130         if (!itinfo || !obj)
131                 return NULL;
132
133         sinfo = itinfo->sinfo;
134
135         img = elm_image_add(obj);
136         if (!img)
137                 return NULL;
138         elm_image_aspect_fixed_set(img, EINA_FALSE);
139
140         if (!strcmp(part, "elm.swallow.icon")) {
141                 path = sinfo->ThumbnailPath();
142                 if (path)
143                         elm_image_file_set(img, path, "NULL");
144                 else {
145                         snprintf(buf, sizeof(buf), "%s/%s", IMAGEDIR,
146                                 MUSIC_IMAGE_DEFAULT_THUMB);
147                         elm_image_file_set(img, buf, "NULL");
148                 }
149         }
150
151         return img;
152 }
153
154
155 void CSongLayout::sm_CbRemoveListItem(void *data, Evas_Object *obj)
156 {
157         free(data);
158 }
159
160
161 void CSongLayout::sm_CbCtxtUpdate(void *dt, enum EActionType type, int lid)
162 {
163         CSongLayout *root = (CSongLayout *)dt;
164
165         if (root)
166                 root->m_OnCtxtUpdate(type, lid);
167 }
168
169
170 void CSongLayout::m_OnCtxtUpdate(EActionType type, int lid)
171 {
172         Eina_List *list = NULL;
173         EAddType mode;
174
175         if (!m->ctxtinfo || !m->ctxtinfo->context)
176                 return;
177
178         if (type == ACTION_TYPE_ADDNEXT)
179                 mode = ADD_TYPE_NEXT;
180         else
181                 mode = ADD_TYPE_END;
182
183         list = eina_list_append(list, m->ctxtinfo->context);
184         if (type == ACTION_TYPE_ADDTO) {
185                 if (!m->mhandle->MediaAddsongsPlaylist(lid, list))
186                         _ERR(" Adding songs to playlist failed ");
187                 else
188                         CCommonUI::CreateMsgBox(m->base, MUSIC_TEXT_ADDTO_MSG);
189
190                 return;
191         }
192
193         m->mhandle->UpdatePlaylist(list, mode);
194         eina_list_free(list);
195
196         if (type == ACTION_TYPE_PLAY) {
197                 m->mhandle->Stop();
198                 m->mhandle->SetCurrentSong(((CSongInfo *)m->ctxtinfo->context)->Id());
199         }
200
201         CCommonUI::UpdatePlaybackView((EAddType)type, Layout(), m->focused_item);
202 }
203
204
205 void CSongLayout::sm_CbCtxtClose(void *dt)
206 {
207         CSongLayout *root = (CSongLayout *)dt;
208
209         if (root)
210                 root->m_OnCtxtClose();
211 }
212
213
214 void CSongLayout::m_OnCtxtClose(void)
215 {
216         m->vmgr->PopView();
217         elm_object_item_focus_set(m->focused_item, EINA_TRUE);
218 }
219
220
221 void CSongLayout::sm_CbItemSelect(void *data, Evas_Object *obj, void *event_info)
222 {
223         CSongLayout *root = (CSongLayout*)data;
224
225         if (root)
226                 root->m_OnItemSelect(obj, event_info);
227 }
228
229 void CSongLayout::m_OnItemSelect(Evas_Object *obj, void *event_info)
230 {
231         SSongItemInfo *itinfo = NULL;
232         char *mediaId = NULL;
233
234         itinfo = m_FindItemInfoFromItem(m->it_infolist, (Elm_Object_Item *)event_info);
235         if (!itinfo) {
236                 _ERR(" no item info found ");
237                 return;
238         }
239
240         mediaId = itinfo->sinfo->Id();
241
242         m->mhandle->Stop();
243         m->mhandle->UpdatePlaylist(m->slist, ADD_TYPE_FRESH);
244         m->mhandle->SetCurrentSong(mediaId);
245
246         m_GotoPlayback();
247 }
248
249
250 int CSongLayout::sm_CbSortArtistAZ(const void *d1, const void *d2)
251 {
252         CSongInfo *sinfo1 = (CSongInfo *)d1;
253         CSongInfo *sinfo2 = (CSongInfo *)d2;
254         char *txt1 = NULL;
255         char *txt2 = NULL;
256
257         txt1 = sinfo1->Artist();
258         if (!txt1)
259                 return 1;
260
261         txt2 = sinfo2->Artist();
262         if (!txt2)
263                 return -1;
264
265         return strcasecmp(txt1, txt2);
266 }
267
268
269 int CSongLayout::sm_CbSortArtistZA(const void *d1, const void *d2)
270 {
271         CSongInfo *sinfo1 = (CSongInfo *)d1;
272         CSongInfo *sinfo2 = (CSongInfo *)d2;
273         char *txt1 = NULL;
274         char *txt2 = NULL;
275
276         txt1 = sinfo1->Artist();
277         if (!txt1)
278                 return -1;
279
280         txt2 = sinfo2->Artist();
281         if (!txt2)
282                 return 1;
283
284         return strcasecmp(txt2, txt1);
285 }
286
287
288 int CSongLayout::sm_CbSortAlbumAZ(const void *d1, const void *d2)
289 {
290         CSongInfo *sinfo1 = (CSongInfo *)d1;
291         CSongInfo *sinfo2 = (CSongInfo *)d2;
292         char *txt1 = NULL;
293         char *txt2 = NULL;
294
295         txt1 = sinfo1->Album();
296         if (!txt1)
297                 return 1;
298
299         txt2 = sinfo2->Album();
300         if (!txt2)
301                 return -1;
302
303         return strcasecmp(txt1, txt2);
304 }
305
306
307 int CSongLayout::sm_CbSortAlbumZA(const void *d1, const void *d2)
308 {
309         CSongInfo *sinfo1 = (CSongInfo *)d1;
310         CSongInfo *sinfo2 = (CSongInfo *)d2;
311         char *txt1 = NULL;
312         char *txt2 = NULL;
313
314         txt1 = sinfo1->Album();
315         if (!txt1)
316                 return -1;
317
318         txt2 = sinfo2->Album();
319         if (!txt2)
320                 return 1;
321
322         return strcasecmp(txt2, txt1);
323 }
324
325
326 SSongItemInfo *CSongLayout::m_FindItemInfoFromItem(Eina_List *list, Elm_Object_Item *item)
327 {
328         Eina_List *l;
329         SSongItemInfo *itinfo = NULL;
330         void *obj = NULL;
331
332         EINA_LIST_FOREACH(list, l, obj) {
333                 itinfo = (SSongItemInfo *)obj;
334                 if (itinfo->item == item)
335                         return itinfo;
336         }
337
338         return NULL;
339 }
340
341
342 void CSongLayout::m_UpdateSongList(bool sort_flag)
343 {
344         bool r;
345         CSongInfo *sinfo;
346         void *obj;
347         Eina_List *l;
348         struct SSongItemInfo *itinfo;
349         Elm_Object_Item *item;
350
351         /* Remove existing songlist and prepare afresh */
352         m_EmptySongList(sort_flag);
353
354         if (!sort_flag) {
355                 r = m->mhandle->MediaGetList(LIST_TYPE_SONG, NULL, &(m->slist));
356                 if (r == false || eina_list_count(m->slist) == 0) {
357                         _ERR(" Fetching song list from media failed ");
358
359                         SetEmptyStatus(true);
360                         return;
361                 }
362         }
363
364         m_SortSongList();
365
366         EINA_LIST_FOREACH(m->slist, l, obj) {
367                 sinfo = (CSongInfo *)obj;
368                 itinfo = (SSongItemInfo *)calloc(1, sizeof(*itinfo));
369                 if (!itinfo)
370                         return;
371
372                 itinfo->sinfo = sinfo;
373                 item = elm_genlist_item_append(m->songlist, m->item_class,
374                         itinfo, NULL, ELM_GENLIST_ITEM_NONE,
375                         sm_CbItemSelect, this);
376                 itinfo->item = item;
377                 m->it_infolist = eina_list_append(m->it_infolist, itinfo);
378         }
379 }
380
381 void CSongLayout::m_SortSongList(void)
382 {
383         int sortType;
384
385         const char *sortFuncId[] = {
386                 SORT_BY_NAME_AZ,
387                 SORT_BY_NAME_ZA,
388                 SORT_BY_ARTIST_AZ,
389                 SORT_BY_ARTIST_ZA,
390                 SORT_BY_ALBUM_AZ,
391                 SORT_BY_ALBUM_ZA
392         };
393
394         sortType = CInfo::SortType();
395
396         m->slist = CSortMgr::Sort(m->slist, sortFuncId[sortType]);
397 }
398
399 void CSongLayout::m_EmptySongList(bool sort_flag)
400 {
401         if (m->songlist)
402                 elm_genlist_clear(m->songlist);
403
404         if (!sort_flag) {
405                 if (m->slist)
406                         eina_list_free(m->slist);
407                 m->slist = NULL;
408         }
409
410         eina_list_free(m->it_infolist);
411         m->it_infolist = NULL;
412 }
413
414
415 void CSongLayout::m_GotoPlayback(void)
416 {
417         SParcel parcel;
418         memset(&parcel, 0, sizeof(SParcel));
419         parcel.updateType = E_PLAYLIST_UPDATE;
420
421         if (!m->vmgr->PushView(MUSIC_PLAYBACK_VIEW, &parcel))
422                 _ERR(" viewmgr  push view  MUSIC_PLAYBACK_VIEW failed ");
423 }
424
425
426 void CSongLayout::m_GotoPlaybackUri(void)
427 {
428         Eina_List *list = NULL;
429         CSongInfo *sinfo = m->mhandle->MediaSongByUri(m->uri);
430         if (!sinfo) {
431                 _ERR(" Fetching song list from uri failed ");
432                 return;
433         }
434
435         list = eina_list_append(list, sinfo);
436         if (!list)
437                 return;
438
439         m->mhandle->UpdatePlaylist(list, ADD_TYPE_FRESH);
440
441         eina_list_free(list);
442
443         m_GotoPlayback();
444 }
445
446
447 void CSongLayout::m_CreateSongList(void)
448 {
449         Evas_Object *genlist = NULL;
450         Elm_Genlist_Item_Class *list_item = NULL;
451
452         genlist = elm_genlist_add(Layout());
453         if (!genlist)
454                 return;
455
456         elm_genlist_homogeneous_set(genlist, EINA_TRUE);
457         evas_object_size_hint_weight_set(genlist,
458                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
459         elm_object_part_content_set(Layout(), MUSIC_PART_SONGLIST, genlist);
460         Connect(genlist, SONG_LAYOUT_GENLIST, TYPE_MOUSE_MOVE | TYPE_KEY_DOWN | TYPE_ACTIVATED);
461         m->songlist = genlist;
462
463         list_item = elm_genlist_item_class_new();
464         if (!list_item) {
465                 evas_object_del(genlist);
466                 _ERR(" elm_genlist_item_class_new failed ");
467                 return;
468         }
469         list_item->item_style = MUSIC_STYLE_SONGLIST;
470         list_item->func.text_get = sm_CbGetListItemText;
471         list_item->func.content_get = sm_CbGetListItemContent;
472         list_item->func.state_get = NULL;
473         list_item->func.del = sm_CbRemoveListItem;
474         m->item_class = list_item;
475
476         m_UpdateSongList(false);
477 }
478
479
480 bool CSongLayout::Create(CLayoutMgr *mgr, const char *uri)
481 {
482         ASSERT(!m);
483         ASSERT(mgr);
484
485         _CREATE_BEGIN{
486                 Evas_Object *base = NULL;
487                 Evas_Object *win = NULL;
488                 Evas_Object *layout = NULL;
489                 CMusicController *mhandle = NULL;
490                 CViewMgr *vmgr = NULL;
491
492                 _CHECK(m = new SSongLayout)
493                 _CHECK(vmgr = CViewMgr::GetInstance())
494                 _CHECK(base = mgr->Base())
495                 _CHECK(win = vmgr->Window())
496                 _CHECK(mhandle = CMusicController::GetInstance())
497                 _CHECK(layout = CCommonUI::AddBase(base, MUSIC_SONG_LAYOUT))
498                 _CHECK(CExtBaseLayout::Create(layout))
499
500                 _WHEN_SUCCESS{
501                         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ARTIST_AZ, sm_CbSortArtistAZ);
502                         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ARTIST_ZA, sm_CbSortArtistZA);
503                         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ALBUM_AZ, sm_CbSortAlbumAZ);
504                         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ALBUM_ZA, sm_CbSortAlbumZA);
505
506                         if (uri) {
507                                 m->uri = new char[strlen(uri) + 1];
508                                 strcpy(m->uri, uri);
509                         }
510                         else
511                                 m->uri = NULL;
512
513                         m->win = win;
514                         m->base = base;
515                         m->vmgr = vmgr;
516                         m->mhandle = mhandle;
517                         m->mgr = mgr;
518
519                         m_CreateSongList();
520
521                         Connect(layout, SONG_LAYOUT, TYPE_KEY_DOWN);
522
523                         SParcel parcel;
524                         memset(&parcel, 0, sizeof(SParcel));
525                         parcel.updateType = E_FOCUS_UPDATE;
526                         parcel.layoutId = MUSIC_SONG_LAYOUT;
527                         m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
528                 }
529
530                 _CHECK_FAIL{ CExtBaseLayout::Destroy(); }
531                 _CHECK_FAIL{ evas_object_del(layout); }
532                 _CHECK_FAIL{}
533                 _CHECK_FAIL{}
534                 _CHECK_FAIL{}
535                 _CHECK_FAIL{}
536                 _CHECK_FAIL{ delete m; m = NULL; }
537         } _CREATE_END_AND_CATCH{ return false; }
538
539         return true;
540 }
541
542 void CSongLayout::Destroy(void)
543 {
544         ASSERT(m);
545
546         Disconnect(Layout());
547
548         CExtBaseLayout::Destroy();
549         evas_object_del(Layout());
550
551         free(m->ctxtinfo);
552         
553         delete[] m->uri;
554         delete m;
555         m = NULL;
556 }
557
558
559 void CSongLayout::t_OnShow(void)
560 {
561         ASSERT(m);
562
563         evas_object_show(Layout());
564         m_UpdateSongList(false);
565
566         if (!m->uri)
567                 return;
568
569         m_GotoPlaybackUri();
570         delete[] m->uri;
571         m->uri = NULL;
572 }
573
574
575 void CSongLayout::Update(bool focusFlag)
576 {
577         ASSERT(m);
578
579         if (!focusFlag) {
580                 m_UpdateSongList(true);
581                 return;
582         }
583
584         elm_object_focus_set(m->songlist, EINA_TRUE);
585 }
586
587
588 void CSongLayout::SetFocus(const char *btnStr)
589 {
590         ASSERT(m);
591
592         Elm_Object_Item *item = elm_genlist_first_item_get(m->songlist);
593         ASSERT(item);
594
595         elm_genlist_item_show(item, ELM_GENLIST_ITEM_SCROLLTO_IN);
596         elm_object_item_focus_set(item, EINA_TRUE);
597 }
598
599
600 void CSongLayout::OnKeyDown(int id, Evas *e, Evas_Object *obj, Evas_Event_Key_Down *ev)
601 {
602         switch (id) {
603         case SONG_LAYOUT:
604                 {
605                         if (!strcmp(ev->keyname, KEY_BACK) ||
606                                 !strcmp(ev->keyname, KEY_BACK_REMOTE)) {
607                                 SParcel parcel;
608                                 memset(&parcel, 0, sizeof(SParcel));
609                                 parcel.updateType = E_FOCUS_UPDATE;
610                                 m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
611                         }
612                 }
613                 break;
614
615         case SONG_LAYOUT_GENLIST:
616                 {
617                         Elm_Object_Item *it = NULL;
618                         SContentInfo *ctxtinfo = NULL;
619                         SSongItemInfo *itinfo = NULL;
620
621                         if (strcmp(ev->keyname, KEY_MENU) &&
622                                 strcmp(ev->keyname, KEY_MENU_REMOTE))
623                                 return;
624
625                         it = elm_object_focused_item_get(obj);
626                         if (!it) {
627                                 _ERR(" unable to get focused item ");
628                                 return;
629                         }
630                         m->focused_item = it;
631
632                         if (m->ctxtinfo) {
633                                 free(m->ctxtinfo);
634                                 m->ctxtinfo = NULL;
635                         }
636
637                         ctxtinfo = (SContentInfo *)calloc(1, sizeof(*ctxtinfo));
638                         if (!ctxtinfo)
639                                 return;
640
641                         itinfo = m_FindItemInfoFromItem(m->it_infolist, it);
642                         if (!itinfo) {
643                                 free(ctxtinfo);
644                                 return;
645                         }
646
647                         ctxtinfo->type = CONTEXT_TYPE_SONG;
648                         ctxtinfo->context = itinfo->sinfo;
649                         ctxtinfo->cbdata = this;
650                         ctxtinfo->update = sm_CbCtxtUpdate;
651                         ctxtinfo->close = sm_CbCtxtClose;
652
653                         m->ctxtinfo = ctxtinfo;
654
655                         SParcel parcel;
656                         memset(&parcel, 0, sizeof(SParcel));
657                         parcel.ctxtInfo = ctxtinfo;
658                         if (!m->vmgr->PushView(MUSIC_CONTEXT_VIEW, &parcel))
659                                 _ERR("viewmgr push view MUSIC_CONTEXT_VIEW failed");
660                 }
661                 break;
662
663         default:
664                 break;
665         }
666 }
667
668
669 void CSongLayout::OnMouseMove(int id, Evas *e, Evas_Object *obj, Evas_Event_Mouse_Move *ev)
670 {
671         switch (id) {
672         case SONG_LAYOUT_GENLIST:
673                 {
674                         Elm_Object_Item *item;
675
676                         item = elm_genlist_at_xy_item_get(obj, ev->cur.canvas.x,
677                                 ev->cur.canvas.y, NULL);
678
679                         if (!elm_object_item_focus_get(item))
680                                 elm_object_item_focus_set(item, EINA_TRUE);
681                 }
682                 break;
683
684         default:
685                 break;
686         }
687 }
688
689
690 void CSongLayout::OnActivated(int id, Evas_Object *obj, Elm_Object_Item *item)
691 {
692         switch (id) {
693         case SONG_LAYOUT_GENLIST:
694                 m_OnItemSelect(obj, item);
695                 break;
696
697         default:
698                 break;
699         }
700 }