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