[JIRA:TT-5] Pause button becomes deactivated after removing playlist in the playback...
[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 <utilX.h>
20 #include "AppCommon.h"
21 #include "InputHandler.h"
22 #include <SortMgr.h>
23 #include <Sort.h>
24 #include "dbg.h"
25 #include "i18n.h"
26 #include "define.h"
27 #include "song_info.h"
28 #include "album_info.h"
29 #include "music-controller.h"
30 #include "LayoutMgr.h"
31 #include "common.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 = _get_timestr_from_milseconds(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                         _create_message_box(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         _update_playback_view((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         m->vmgr->PushView(MUSIC_BASE_VIEW, NULL);
218         elm_object_item_focus_set(m->focused_item, EINA_TRUE);
219 }
220
221
222 void CSongLayout::sm_CbItemSelect(void *data, Evas_Object *obj, void *event_info)
223 {
224         CSongLayout *root = (CSongLayout*)data;
225
226         if (root)
227                 root->m_OnItemSelect(obj, event_info);
228 }
229
230 void CSongLayout::m_OnItemSelect(Evas_Object *obj, void *event_info)
231 {
232         SSongItemInfo *itinfo = NULL;
233         char *mediaId = NULL;
234
235         itinfo = m_FindItemInfoFromItem(m->it_infolist, (Elm_Object_Item *)event_info);
236         if (!itinfo) {
237                 _ERR(" no item info found ");
238                 return;
239         }
240
241         mediaId = itinfo->sinfo->Id();
242
243         m->mhandle->Stop();
244         m->mhandle->UpdatePlaylist(m->slist, ADD_TYPE_FRESH);
245         m->mhandle->SetCurrentSong(mediaId);
246
247         m_GotoPlayback();
248 }
249
250
251 int CSongLayout::sm_CbSortArtistAZ(const void *d1, const void *d2)
252 {
253         CSongInfo *sinfo1 = (CSongInfo *)d1;
254         CSongInfo *sinfo2 = (CSongInfo *)d2;
255         char *txt1 = NULL;
256         char *txt2 = NULL;
257
258         txt1 = sinfo1->Artist();
259         if (!txt1)
260                 return 1;
261
262         txt2 = sinfo2->Artist();
263         if (!txt2)
264                 return -1;
265
266         return strcasecmp(txt1, txt2);
267 }
268
269
270 int CSongLayout::sm_CbSortArtistZA(const void *d1, const void *d2)
271 {
272         CSongInfo *sinfo1 = (CSongInfo *)d1;
273         CSongInfo *sinfo2 = (CSongInfo *)d2;
274         char *txt1 = NULL;
275         char *txt2 = NULL;
276
277         txt1 = sinfo1->Artist();
278         if (!txt1)
279                 return -1;
280
281         txt2 = sinfo2->Artist();
282         if (!txt2)
283                 return 1;
284
285         return strcasecmp(txt2, txt1);
286 }
287
288
289 int CSongLayout::sm_CbSortAlbumAZ(const void *d1, const void *d2)
290 {
291         CSongInfo *sinfo1 = (CSongInfo *)d1;
292         CSongInfo *sinfo2 = (CSongInfo *)d2;
293         char *txt1 = NULL;
294         char *txt2 = NULL;
295
296         txt1 = sinfo1->Album();
297         if (!txt1)
298                 return 1;
299
300         txt2 = sinfo2->Album();
301         if (!txt2)
302                 return -1;
303
304         return strcasecmp(txt1, txt2);
305 }
306
307
308 int CSongLayout::sm_CbSortAlbumZA(const void *d1, const void *d2)
309 {
310         CSongInfo *sinfo1 = (CSongInfo *)d1;
311         CSongInfo *sinfo2 = (CSongInfo *)d2;
312         char *txt1 = NULL;
313         char *txt2 = NULL;
314
315         txt1 = sinfo1->Album();
316         if (!txt1)
317                 return -1;
318
319         txt2 = sinfo2->Album();
320         if (!txt2)
321                 return 1;
322
323         return strcasecmp(txt2, txt1);
324 }
325
326
327 SSongItemInfo *CSongLayout::m_FindItemInfoFromItem(Eina_List *list, Elm_Object_Item *item)
328 {
329         Eina_List *l;
330         SSongItemInfo *itinfo = NULL;
331         void *obj = NULL;
332
333         EINA_LIST_FOREACH(list, l, obj) {
334                 itinfo = (SSongItemInfo *)obj;
335                 if (itinfo->item == item)
336                         return itinfo;
337         }
338
339         return NULL;
340 }
341
342
343 void CSongLayout::m_UpdateSongList(bool sort_flag)
344 {
345         bool r;
346         CSongInfo *sinfo;
347         void *obj;
348         Eina_List *l;
349         struct SSongItemInfo *itinfo;
350         Elm_Object_Item *item;
351
352         /* Remove existing songlist and prepare afresh */
353         m_EmptySongList(sort_flag);
354
355         if (!sort_flag) {
356                 r = m->mhandle->MediaGetList(LIST_TYPE_SONG, NULL, &(m->slist));
357                 if (r == false || eina_list_count(m->slist) == 0) {
358                         _ERR(" Fetching song list from media failed ");
359
360                         SetEmptyStatus(true);
361                         return;
362                 }
363         }
364
365         m_SortSongList();
366
367         EINA_LIST_FOREACH(m->slist, l, obj) {
368                 sinfo = (CSongInfo *)obj;
369                 itinfo = (SSongItemInfo *)calloc(1, sizeof(*itinfo));
370                 if (!itinfo)
371                         return;
372
373                 itinfo->sinfo = sinfo;
374                 item = elm_genlist_item_append(m->songlist, m->item_class,
375                         itinfo, NULL, ELM_GENLIST_ITEM_NONE,
376                         sm_CbItemSelect, this);
377                 itinfo->item = item;
378                 m->it_infolist = eina_list_append(m->it_infolist, itinfo);
379         }
380 }
381
382 void CSongLayout::m_SortSongList(void)
383 {
384         int sortType;
385
386         const char *sortFuncId[] = {
387                 SORT_BY_NAME_AZ,
388                 SORT_BY_NAME_ZA,
389                 SORT_BY_ARTIST_AZ,
390                 SORT_BY_ARTIST_ZA,
391                 SORT_BY_ALBUM_AZ,
392                 SORT_BY_ALBUM_ZA
393         };
394
395         sortType = CInfo::SortType();
396
397         m->slist = CSort::Sort(m->slist, sortFuncId[sortType]);
398 }
399
400 void CSongLayout::m_EmptySongList(bool sort_flag)
401 {
402         if (m->songlist)
403                 elm_genlist_clear(m->songlist);
404
405         if (!sort_flag) {
406                 if (m->slist)
407                         eina_list_free(m->slist);
408                 m->slist = NULL;
409         }
410
411         eina_list_free(m->it_infolist);
412         m->it_infolist = NULL;
413 }
414
415
416 void CSongLayout::m_GotoPlayback(void)
417 {
418         SParcel parcel;
419         parcel.ctxtInfo = NULL;
420         parcel.updateType = E_PLAYLIST_UPDATE;
421         parcel.layoutId = NULL;
422         parcel.keyEvent = NULL;
423
424         if (!m->vmgr->PushView(MUSIC_PLAYBACK_VIEW, &parcel))
425                 _ERR(" viewmgr  push view  MUSIC_PLAYBACK_VIEW failed ");
426 }
427
428
429 void CSongLayout::m_GotoPlaybackUri(void)
430 {
431         Eina_List *list = NULL;
432         CSongInfo *sinfo = m->mhandle->MediaSongByUri(m->uri);
433         if (!sinfo) {
434                 _ERR(" Fetching song list from uri failed ");
435                 return;
436         }
437
438         list = eina_list_append(list, sinfo);
439         if (!list)
440                 return;
441
442         m->mhandle->UpdatePlaylist(list, ADD_TYPE_FRESH);
443
444         eina_list_free(list);
445
446         m_GotoPlayback();
447 }
448
449
450 void CSongLayout::m_CreateSongList(void)
451 {
452         Evas_Object *genlist = NULL;
453         Elm_Genlist_Item_Class *list_item = NULL;
454
455         genlist = elm_genlist_add(Layout());
456         if (!genlist)
457                 return;
458
459         elm_genlist_homogeneous_set(genlist, EINA_TRUE);
460         evas_object_size_hint_weight_set(genlist,
461                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
462         elm_object_part_content_set(Layout(), MUSIC_PART_SONGLIST, genlist);
463         Connect(genlist, SONG_LAYOUT_GENLIST, TYPE_MOUSE_MOVE | TYPE_KEY_DOWN | TYPE_ACTIVATED);
464         m->songlist = genlist;
465
466         list_item = elm_genlist_item_class_new();
467         if (!list_item) {
468                 evas_object_del(genlist);
469                 _ERR(" elm_genlist_item_class_new failed ");
470                 return;
471         }
472         list_item->item_style = MUSIC_STYLE_SONGLIST;
473         list_item->func.text_get = sm_CbGetListItemText;
474         list_item->func.content_get = sm_CbGetListItemContent;
475         list_item->func.state_get = NULL;
476         list_item->func.del = sm_CbRemoveListItem;
477         m->item_class = list_item;
478
479         m_UpdateSongList(false);
480 }
481
482
483 bool CSongLayout::Create(CLayoutMgr *mgr, const char *uri)
484 {
485         ASSERT(!m);
486
487         Evas_Object *base = NULL;
488         Evas_Object *win = NULL;
489         Evas_Object *layout = NULL;
490         CMusicController *mhandle = NULL;
491         CViewMgr *vmgr = NULL;
492
493         if (!mgr)
494                 return false;
495
496         m = new SSongLayout;
497         if (!m) {
498                 _ERR(" calloc failed ");
499                 return false;
500         }
501
502         vmgr = CViewMgr::GetInstance();
503         if (!vmgr) {
504                 _ERR(" get view mgr failed ");
505                 delete m;
506                 m = NULL;
507                 return false;
508         }
509
510         base = mgr->Base();
511         if (!base) {
512                 _ERR(" get base failed ");
513                 delete m;
514                 m = NULL;
515                 return false;
516         }
517
518         win = vmgr->Window();
519         if (!win) {
520                 _ERR(" vimewmgr get win failed ");
521                 delete m;
522                 m = NULL;
523                 return false;
524         }
525
526         mhandle = CMusicController::GetInstance();
527         if (!mhandle) {
528                 _ERR(" music controller get music instance failed ");
529                 delete m;
530                 m = NULL;
531                 return false;
532         }
533
534         layout = _add_base(base, MUSIC_SONG_LAYOUT);
535         if (!layout) {
536                 _ERR(" get layout failed ");
537                 delete m;
538                 m = NULL;
539                 return false;
540         }
541
542         bool r = CExtBaseLayout::Create(layout);
543         if (!r) {
544                 _ERR(" CBaseLayout creation failed ");
545                 evas_object_del(layout);
546                 delete m;
547                 m = NULL;
548                 return false;
549         }
550
551         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ARTIST_AZ, sm_CbSortArtistAZ);
552         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ARTIST_ZA, sm_CbSortArtistZA);
553         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ALBUM_AZ, sm_CbSortAlbumAZ);
554         CSortMgr::GetInstance()->RegisterSortFunction(SORT_BY_ALBUM_ZA, sm_CbSortAlbumZA);
555
556         if (uri) {
557                 m->uri = new char[strlen(uri) + 1];
558                 strcpy(m->uri, uri);
559         }
560         else {
561                 m->uri = NULL;
562         }
563
564         m->win = win;
565         m->base = base;
566         m->vmgr = vmgr;
567         m->mhandle = mhandle;
568         m->mgr = mgr;
569
570         m_CreateSongList();
571
572         Connect(layout, SONG_LAYOUT, TYPE_KEY_DOWN);
573
574         SParcel parcel;
575         parcel.ctxtInfo = NULL;
576         parcel.updateType = E_FOCUS_UPDATE;
577         parcel.layoutId = MUSIC_SONG_LAYOUT;
578         parcel.keyEvent = NULL;
579         m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
580
581         return true;
582 }
583
584 void CSongLayout::Destroy(void)
585 {
586         ASSERT(m);
587
588         CExtBaseLayout::Destroy();
589         evas_object_del(Layout());
590
591         free(m->ctxtinfo);
592         delete[] m->uri;
593         delete m;
594         m = NULL;
595 }
596
597
598 void CSongLayout::t_OnShow(void)
599 {
600         ASSERT(m);
601
602         evas_object_show(Layout());
603
604         if (!m->uri)
605                 return;
606
607         m_GotoPlaybackUri();
608         delete[] m->uri;
609         m->uri = NULL;
610 }
611
612
613 void CSongLayout::Update(bool focusFlag)
614 {
615         ASSERT(m);
616
617         if (!focusFlag) {
618                 m_UpdateSongList(true);
619                 return;
620         }
621
622         elm_object_focus_set(m->songlist, EINA_TRUE);
623 }
624
625
626 void CSongLayout::OnKeyDown(int id, Evas *e, Evas_Object *obj, Evas_Event_Key_Down *ev)
627 {
628         switch (id) {
629         case SONG_LAYOUT:
630                 {
631                         if (!strcmp(ev->keyname, KEY_BACK) ||
632                                 !strcmp(ev->keyname, KEY_BACK_REMOTE)) {
633                                 SParcel parcel;
634                                 parcel.ctxtInfo = NULL;
635                                 parcel.updateType = E_FOCUS_UPDATE;
636                                 parcel.layoutId = NULL;
637                                 parcel.keyEvent = NULL;
638                                 m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
639                         }
640                 }
641                 break;
642
643         case SONG_LAYOUT_GENLIST:
644                 {
645                         Elm_Object_Item *it = NULL;
646                         SContentInfo *ctxtinfo = NULL;
647                         SSongItemInfo *itinfo = NULL;
648
649                         if (strcmp(ev->keyname, KEY_MENU) &&
650                                 strcmp(ev->keyname, KEY_MENU_REMOTE))
651                                 return;
652
653                         it = elm_object_focused_item_get(obj);
654                         if (!it) {
655                                 _ERR(" unable to get focused item ");
656                                 return;
657                         }
658                         m->focused_item = it;
659
660                         if (m->ctxtinfo) {
661                                 free(m->ctxtinfo);
662                                 m->ctxtinfo = NULL;
663                         }
664
665                         ctxtinfo = (SContentInfo *)calloc(1, sizeof(*ctxtinfo));
666                         if (!ctxtinfo)
667                                 return;
668
669                         itinfo = m_FindItemInfoFromItem(m->it_infolist, it);
670                         if (!itinfo) {
671                                 free(ctxtinfo);
672                                 return;
673                         }
674
675                         ctxtinfo->type = CONTEXT_TYPE_SONG;
676                         ctxtinfo->context = itinfo->sinfo;
677                         ctxtinfo->cbdata = this;
678                         ctxtinfo->update = sm_CbCtxtUpdate;
679                         ctxtinfo->close = sm_CbCtxtClose;
680
681                         m->ctxtinfo = ctxtinfo;
682
683                         SParcel parcel;
684                         parcel.ctxtInfo = ctxtinfo;
685                         if (!m->vmgr->PushView(MUSIC_CONTEXT_VIEW, &parcel))
686                                 _ERR("viewmgr push view MUSIC_CONTEXT_VIEW failed");
687                 }
688                 break;
689
690         default:
691                 break;
692         }
693 }
694
695
696 void CSongLayout::OnMouseMove(int id, Evas *e, Evas_Object *obj, Evas_Event_Mouse_Move *ev)
697 {
698         switch (id) {
699         case SONG_LAYOUT_GENLIST:
700                 {
701                         Elm_Object_Item *item;
702
703                         item = elm_genlist_at_xy_item_get(obj, ev->cur.canvas.x,
704                                 ev->cur.canvas.y, NULL);
705
706                         if (!elm_object_item_focus_get(item))
707                                 elm_object_item_focus_set(item, EINA_TRUE);
708                 }
709                 break;
710
711         default:
712                 break;
713         }
714 }
715
716
717 void CSongLayout::OnActivated(int id, Evas_Object *obj, Elm_Object_Item *item)
718 {
719         switch (id) {
720         case SONG_LAYOUT_GENLIST:
721                 m_OnItemSelect(obj, item);
722                 break;
723
724         default:
725                 break;
726         }
727 }