/* * 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 #include #include #include #include "define.h" #include "view.h" #include "util/controller.h" #include "util/playermgr.h" #include "util/progressbar.h" #include "util/util.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define STYLE_MUSIC_BTN "music_btn" #define PART_MUSIC_BTN "control_btn" #define BTN_LOC_SHUFFLE 0 #define BTN_LOC_PREV 1 #define BTN_LOC_PLAY 2 #define BTN_LOC_NEXT 3 #define BTN_LOC_REPEAT 4 enum _repeat_state { REPEAT_ALL, REPEAT_ONE, REPEAT_NONE }; struct _list_data { app_media *am; Elm_Object_Item *item; }; struct _playlist { Eina_List *list; Eina_List *list_org; int cur; bool shuffle; enum _repeat_state repeat; }; struct _priv { Evas_Object *win; Evas_Object *base; Evas_Object *thumb; Evas_Object *list; Eina_List *item_list; struct _playlist playlist; struct playermgr *player; struct progressbar *progress; struct controller *ctl; const char *prev_view; }; struct _btn_info { const char *name; int loc; }; static struct _btn_info btn_player[] = { { .name = SRC_BTN_MUSIC_SHUFFLE, .loc = BTN_LOC_SHUFFLE, }, { .name = SRC_BTN_MUSIC_PREV, .loc = BTN_LOC_PREV, }, { .name = SRC_BTN_MUSIC_PLAY, .loc = BTN_LOC_PLAY, }, { .name = SRC_BTN_MUSIC_NEXT, .loc = BTN_LOC_NEXT, }, { .name = SRC_BTN_MUSIC_REPEAT, .loc = BTN_LOC_REPEAT, }, }; static void _draw_playlist(struct _priv *priv) { struct _list_data *ld; ld = eina_list_nth(priv->item_list, priv->playlist.cur); if (!ld) { _ERR("failed to get list data"); return; } if (!elm_object_focus_get(priv->list)) { elm_genlist_item_bring_in(ld->item, ELM_GENLIST_ITEM_SCROLLTO_IN); } elm_genlist_realized_items_update(priv->list); } static void _draw_thumbnail(struct _priv *priv, app_media_info *mi) { char *path; if (mi->thumbnail_path) path = mi->thumbnail_path; else path = IMAGE_MUSIC_THUMBNAIL; elm_image_file_set(priv->thumb, path, NULL); elm_object_part_content_set(priv->base, PART_MPLAYER_THUMB, priv->thumb); } static void _draw_music_info(struct _priv *priv, app_media_info *mi) { char buf[128] = {0,}; snprintf(buf, sizeof(buf), "%s / %s", mi->audio->artist, mi->audio->album); elm_object_part_text_set(priv->base, PART_MPLAYER_TITLE, mi->title); elm_object_part_text_set(priv->base, PART_MPLAYER_INFO, buf); } static void _draw_progressbar(struct _priv *priv, app_media_info *mi) { progressbar_show(priv->progress); } static app_media *_playlist_get_media(Eina_List *list, int index) { app_media *am; am = eina_list_nth(list, index); if (!am) { _ERR("failed to get app_media"); return NULL; } return am; } static app_media_info *_playlist_get_current_media_info(struct _priv *priv) { app_media *am; app_media_info *mi; am = eina_list_nth(priv->playlist.list, priv->playlist.cur); if (!am) { _ERR("failed to get app_media"); return NULL; } mi = app_media_get_info(am); if (!mi) { _ERR("failed to getting media info"); return NULL; } return mi; } static const char *_playlist_get_current_media_id(struct _priv *priv) { app_media_info *mi; mi = _playlist_get_current_media_info(priv); if (!mi) return NULL; return mi->media_id; } static int _playlist_get_index(Eina_List *dest, Eina_List *src, int index) { app_media *am; am = _playlist_get_media(src, index); return util_get_media_index(dest, am); } static void _playlist_set_list(struct _priv *priv, bool shuffle, int index) { struct _playlist *p; app_media *am; p = &priv->playlist; if (!p) { _ERR("playlist is NULL"); return; } am = _playlist_get_media(p->list, index); eina_list_free(p->list); p->list = eina_list_clone(p->list_org); if (shuffle) p->list = eina_list_shuffle(p->list, NULL); p->cur = util_get_media_index(p->list, am); p->shuffle = shuffle; } static void _playlist_set_repeat_mode(struct _priv *priv) { struct _playlist *p; p = &priv->playlist; if (!p) { _ERR("playlist is NULL"); return; } switch (p->repeat) { case REPEAT_ALL: p->repeat = REPEAT_ONE; break; case REPEAT_ONE: p->repeat = REPEAT_NONE; break; case REPEAT_NONE: p->repeat = REPEAT_ALL; break; default: _ERR("Unknown repeat state"); p->repeat = REPEAT_ALL; break; } } static void _update_info(struct _priv *priv, app_media_info *mi) { struct view_update_data vdata; /* update progressbar */ progressbar_reset(priv->progress, 0, mi->audio->duration); /* update history */ util_add_to_recent(priv->playlist.list, priv->playlist.cur); /* update play info */ vdata.index = _playlist_get_index(priv->playlist.list_org, priv->playlist.list, priv->playlist.cur); vdata.id = _playlist_get_current_media_id(priv); viewmgr_update_view(VIEW_BASE, UPDATE_PLAY_INFO, &vdata); viewmgr_update_view(VIEW_DETAIL, UPDATE_PLAY_INFO, &vdata); } static void _update_player(struct _priv *priv) { struct view_update_data vdata; player_state_e state; playermgr_get_state(priv->player, &state); switch (state) { case PLAYER_STATE_PAUSED: vdata.status = E_PLAYER_PAUSE; break; case PLAYER_STATE_PLAYING: vdata.status = E_PLAYER_PLAY; break; case PLAYER_STATE_IDLE: case PLAYER_STATE_READY: default: vdata.status = E_PLAYER_STOP; break; } /* update player */ viewmgr_update_view(VIEW_BASE, UPDATE_PLAYER, &vdata); viewmgr_update_view(VIEW_DETAIL, UPDATE_PLAYER, &vdata); } static void _mplayer_show(struct _priv *priv) { struct controller *ctl; app_media_info *mi; mi = _playlist_get_current_media_info(priv); if (!mi) { _ERR("failed to getting media info"); return; } if (viewmgr_get_view_state(VIEW_MPLAYER) != VIEW_STATE_VISIBLE) { _update_info(priv, mi); return; } ctl = priv->ctl; ctl->ops->show(ctl->handle); _draw_thumbnail(priv, mi); _draw_music_info(priv, mi); _draw_progressbar(priv, mi); _draw_playlist(priv); _update_info(priv, mi); } static void _mplayer_hide(struct _priv *priv) { struct controller *ctl; ctl = priv->ctl; ctl->ops->hide(ctl->handle); } static bool _mplayer_prev(struct _priv *priv) { int total; total = eina_list_count(priv->playlist.list); switch (priv->playlist.repeat) { case REPEAT_ALL: if (priv->playlist.cur != 0) priv->playlist.cur--; else priv->playlist.cur = total - 1; break; case REPEAT_NONE: if (priv->playlist.cur != 0) priv->playlist.cur--; break; case REPEAT_ONE: break; default: _ERR("Unknown repeat state"); break; } _mplayer_show(priv); return true; } static bool _mplayer_next(struct _priv *priv) { int total; bool r; total = eina_list_count(priv->playlist.list); r = true; switch (priv->playlist.repeat) { case REPEAT_ALL: if (priv->playlist.cur != total - 1) priv->playlist.cur++; else priv->playlist.cur = 0; break; case REPEAT_NONE: if (priv->playlist.cur != total - 1) priv->playlist.cur++; else { priv->playlist.cur = 0; r = false; } break; case REPEAT_ONE: break; default: _ERR("Unknown repeat state"); break; } _mplayer_show(priv); return r; } static void _mplayer_set_current(struct _priv *priv, int index) { int total; if (index < 0) return; total = eina_list_count(priv->playlist.list); if (index >= total) return; priv->playlist.cur = index; _mplayer_show(priv); } static int _player_get_position(void *data) { struct _priv *priv; if (!data) return 0; priv = data; return playermgr_get_position(priv->player); } static void _player_play_pause(struct _priv *priv) { app_media_info *mi; player_state_e state; playermgr_get_state(priv->player, &state); switch (state) { case PLAYER_STATE_PAUSED: progressbar_resume(priv->progress); playermgr_resume(priv->player); break; case PLAYER_STATE_PLAYING: progressbar_pause(priv->progress); playermgr_pause(priv->player); break; case PLAYER_STATE_IDLE: case PLAYER_STATE_READY: mi = _playlist_get_current_media_info(priv); if (!mi) { _ERR("failed to getting media info"); return; } progressbar_start(priv->progress); playermgr_play(priv->player, mi->file_path, 0); break; default: _ERR("player was not created"); break; } _update_player(priv); } static void _player_next(struct _priv *priv) { app_media_info *mi; player_state_e state; playermgr_get_state(priv->player, &state); switch (state) { case PLAYER_STATE_PAUSED: case PLAYER_STATE_IDLE: case PLAYER_STATE_READY: progressbar_stop(priv->progress); playermgr_stop(priv->player); break; case PLAYER_STATE_PLAYING: mi = _playlist_get_current_media_info(priv); if (!mi) { _ERR("failed to getting media info"); return; } progressbar_start(priv->progress); playermgr_play(priv->player, mi->file_path, 0); break; default: _ERR("player was not created"); break; } _update_player(priv); } static void _player_pause(struct _priv *priv) { player_state_e state; struct controller *ctl; playermgr_get_state(priv->player, &state); switch (state) { case PLAYER_STATE_PLAYING: progressbar_pause(priv->progress); playermgr_pause(priv->player); break; case PLAYER_STATE_PAUSED: case PLAYER_STATE_IDLE: case PLAYER_STATE_READY: break; default: _ERR("player was not created"); break; } ctl = priv->ctl; ctl->ops->signal(ctl->handle, BTN_LOC_PLAY, SIG_SET_PLAY); _update_player(priv); } static void _player_play(struct _priv *priv) { app_media_info *mi; player_state_e state; const char *path; struct controller *ctl; mi = _playlist_get_current_media_info(priv); if (!mi) { _ERR("failed to getting media info"); return; } playermgr_get_state(priv->player, &state); switch (state) { case PLAYER_STATE_IDLE: case PLAYER_STATE_READY: progressbar_start(priv->progress); playermgr_play(priv->player, mi->file_path, 0); break; case PLAYER_STATE_PAUSED: path = playermgr_get_path(priv->player); if (path && !strcmp(mi->file_path, path)) playermgr_resume(priv->player); else playermgr_play(priv->player, mi->file_path, 0); progressbar_resume(priv->progress); break; case PLAYER_STATE_PLAYING: path = playermgr_get_path(priv->player); if (path && !strcmp(mi->file_path, path)) break; progressbar_start(priv->progress); playermgr_play(priv->player, mi->file_path, 0); break; default: _ERR("player was not created"); break; } ctl = priv->ctl; ctl->ops->signal(ctl->handle, BTN_LOC_PLAY, SIG_SET_PAUSE); _update_player(priv); } static void _player_stop(struct _priv *priv) { struct controller *ctl; progressbar_stop(priv->progress); playermgr_stop(priv->player); ctl = priv->ctl; ctl->ops->signal(ctl->handle, BTN_LOC_PLAY, SIG_SET_PLAY); _update_player(priv); } static void _player_complete_cb(void *data) { struct _priv *priv; bool r; if (!data) return; priv = data; r = _mplayer_next(priv); if (r) _player_next(priv); else _player_stop(priv); } static struct progressbar_ops _progressbar_ops = { .get_value = _player_get_position, }; static void _pop_view(struct _priv *priv) { struct view_update_data vdata; if (!priv->prev_view) { viewmgr_pop_view(); ui_app_exit(); return; } vdata.index = _playlist_get_index(priv->playlist.list_org, priv->playlist.list, priv->playlist.cur); vdata.id = _playlist_get_current_media_id(priv); viewmgr_update_view(VIEW_BASE, UPDATE_FOCUS, &vdata); viewmgr_pop_view(); } static void _key_down(int id, void *data, Evas *e, Evas_Object *obj, Evas_Event_Key_Down *ev) { struct _priv *priv; bool r; if (!data || !ev) return; priv = data; if (!strcmp(ev->keyname, KEY_BACK) || !strcmp(ev->keyname, KEY_ESC)) { _pop_view(priv); } else if (!strcmp(ev->keyname, KEY_PLAY)) { _player_play(priv); } else if (!strcmp(ev->keyname, KEY_PAUSE)) { _player_pause(priv); } else if (!strcmp(ev->keyname, KEY_STOP)) { _player_stop(priv); } else if (!strcmp(ev->keyname, KEY_NEXT)) { r = _mplayer_next(priv); if (r) _player_next(priv); else _player_stop(priv); } else if (!strcmp(ev->keyname, KEY_PREVIOUS)) { _mplayer_prev(priv); _player_next(priv); } } static void _list_set_focus(struct _priv *priv) { struct _list_data *ld; ld = eina_list_nth(priv->item_list, priv->playlist.cur); if (!ld) { _ERR("failed to get list data"); return; } elm_object_item_focus_set(ld->item, EINA_TRUE); } static void _list_free(struct _priv *priv) { elm_genlist_clear(priv->list); eina_list_free(priv->item_list); priv->item_list = NULL; } static void _list_item_del(void *data, Evas_Object *obj) { free(data); } static char *_list_text_get(void *data, Evas_Object *obj, const char *part) { struct _list_data *ld; app_media_info *mi; int index; char buf[1024]; if (!data) return NULL; ld = data; index = elm_genlist_item_index_get(ld->item); mi = app_media_get_info(ld->am); if (!mi) { _ERR("failed to get media info"); return NULL; } if (!strcmp(part, PART_ELM_TEXT_TITLE) || !strcmp(part, PART_ELM_TEXT_TITLE2)) { snprintf(buf, sizeof(buf), "%02d. %s", index, mi->title); } else if (!strcmp(part, PART_ELM_TEXT_ARTIST)) { snprintf(buf, sizeof(buf), "%s / %s", mi->audio->artist, mi->audio->album); } else if (!strcmp(part, PART_ELM_TEXT_PLAYTIME)) { util_time_string(buf, sizeof(buf), mi->audio->duration, false); } else { return NULL; } return strdup(buf); } static Evas_Object *_list_content_get(void *data, Evas_Object *obj, const char *part) { struct _list_data *ld; app_media_info *mi; Evas_Object *image; if (!data) return NULL; ld = data; mi = app_media_get_info(ld->am); if (!mi) { _ERR("failed to get media info"); return NULL; } image = NULL; if (!strcmp(part, PART_ELM_SWALLOW_FAVORITE)) { if (!mi->favorite) return NULL; image = util_add_image(obj, IMAGE_MUSIC_LIST_FAVORITE); if (!image) { _ERR("failed to create image object"); return NULL; } evas_object_show(image); } return image; } static void _list_selected(int id, void *data, Evas_Object *obj, Elm_Object_Item *it) { struct _priv *priv; int index; if (!data || !it) return; priv = data; index = elm_genlist_item_index_get(it) - 1; if (priv->playlist.cur != index) _mplayer_set_current(priv, index); _player_play(priv); } static void _list_realized(int id, void *data, Evas_Object *obj, Elm_Object_Item *it) { struct _priv *priv; int index; if (!data || !it) return; priv = data; index = elm_genlist_item_index_get(it) - 1; if (priv->playlist.cur == index) { elm_object_item_signal_emit(it, SIG_ITEM_SELECTED, SIG_SOURCE_SRC); } else { elm_object_item_signal_emit(it, SIG_ITEM_UNSELECTED, SIG_SOURCE_SRC); } } static void _list_unrealized(int id, void *data, Evas_Object *obj, Elm_Object_Item *it) { if (!it) return; elm_object_item_signal_emit(it, SIG_ELM_UNFOCUSED, SIG_SOURCE_ELM); } static void _list_mouse_move(int id, void *data, Evas *e, Evas_Object *obj, Evas_Event_Mouse_Move *ev) { Elm_Object_Item *it; if (!data || !ev) return; it = elm_genlist_at_xy_item_get(obj, ev->cur.canvas.x, ev->cur.canvas.y, NULL); if (!it) return; if (!elm_object_item_focus_get(it)) elm_object_item_focus_set(it, EINA_TRUE); } static input_handler _list_handler = { .realized = _list_realized, .unrealized = _list_unrealized, .selected = _list_selected, .mouse_move = _list_mouse_move, .key_down = _key_down, }; static input_handler _btn_handler = { .key_down = _key_down, }; static void _add_playlist_item(struct _priv *priv) { Elm_Genlist_Item_Class *ic; Elm_Object_Item *item; app_media *am; int i; int total; _list_free(priv); ic = elm_genlist_item_class_new(); if (!ic) { _ERR("failed to get genlist item class"); return; } ic->item_style = STYLE_MPLAYER_LIST; ic->func.text_get = _list_text_get; ic->func.content_get = _list_content_get; ic->func.state_get = NULL; ic->func.del = _list_item_del; total = eina_list_count(priv->playlist.list); for (i = 0; i < total; i++) { struct _list_data *ld; ld = calloc(1, sizeof(*ld)); if (!ld) continue; am = eina_list_nth(priv->playlist.list, i); ld->am = am; item = elm_genlist_item_append(priv->list, ic, ld, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); ld->item = item; priv->item_list = eina_list_append(priv->item_list, ld); } elm_genlist_item_class_free(ic); } static void _callback_music(void *data, const char *ev) { struct _priv *priv; bool r; if (!data || !ev) return; priv = data; if (!strcmp(ev, SRC_BTN_MUSIC_PREV)) { _mplayer_prev(priv); _player_next(priv); } else if (!strcmp(ev, SRC_BTN_MUSIC_NEXT)) { r = _mplayer_next(priv); if (r) _player_next(priv); else _player_stop(priv); } else if (!strcmp(ev, SRC_BTN_MUSIC_PLAY)) { _player_play_pause(priv); } else if (!strcmp(ev, SRC_BTN_MUSIC_SHUFFLE)) { _playlist_set_list(priv, !priv->playlist.shuffle, priv->playlist.cur); _add_playlist_item(priv); _mplayer_set_current(priv, priv->playlist.cur); _player_play(priv); } else if (!strcmp(ev, SRC_BTN_MUSIC_REPEAT)) { _playlist_set_repeat_mode(priv); } } static bool _ui_init(struct _priv *priv) { struct controller *ctl; struct progressbar *prog; Evas_Object *obj; int i; obj = elm_image_add(priv->base); if (!obj) { _ERR("failed to adding image"); return false; } priv->thumb = obj; ctl = controller_create(priv->base); if (!ctl) { _ERR("failed to create controller"); return false; } for (i = 0; i < ARRAY_SIZE(btn_player); i++) { ctl->ops->add_control(ctl->handle, btn_player[i].name, btn_player[i].loc, STYLE_MUSIC_BTN, PART_MUSIC_BTN); obj = ctl->ops->get_object(ctl->handle, i); if (obj) inputmgr_add_callback(obj, 0, &_btn_handler, priv); } ctl->ops->add_callback(ctl->handle, _callback_music, priv); priv->ctl = ctl; prog = progressbar_create(priv->base, STYLE_MPLAYER_PROGRESS); if (!prog) { _ERR("failed to adding progressbar"); goto err; } progressbar_set_parts(prog, PART_MPLAYER_SLIDER, PART_MPLAYER_TOTAL, PART_MPLAYER_PROGRESS, ""); progressbar_set_ops(prog, &_progressbar_ops, priv); priv->progress = prog; obj = util_add_genlist(priv->base); if (!obj) goto err; elm_object_part_content_set(priv->base, PART_MPLAYER_LIST, obj); inputmgr_add_callback(obj, 0, &_list_handler, priv); priv->list = obj; return true; err: controller_destroy(ctl); return false; } static Evas_Object *_create(Evas_Object *win, void *data) { struct _priv *priv; Evas_Object *base; struct playermgr *player; bool r; 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"); goto err; } elm_layout_file_set(base, EDJEFILE, GRP_MPLAYER_VIEW); evas_object_size_hint_weight_set(base, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(win, base); priv->win = win; priv->base = base; priv->playlist.list = NULL; priv->playlist.list_org = NULL; priv->playlist.cur = 0; priv->playlist.shuffle = false; priv->playlist.repeat = REPEAT_ALL; player = playermgr_create(NULL); if (!player) { _ERR("failed to create player"); evas_object_del(base); goto err; } r = playermgr_set_completed_cb(player, _player_complete_cb, priv); if (!r) { _ERR("failed to set callback"); playermgr_destroy(player); evas_object_del(base); goto err; } priv->player = player; r = _ui_init(priv); if (!r) { _ERR("failed to init UI"); playermgr_destroy(player); evas_object_del(base); goto err; } viewmgr_set_view_data(VIEW_MPLAYER, priv); return base; err: free(priv); return NULL; } static Eina_Bool _show_view(void *data) { struct _priv *priv; if (!data) return ECORE_CALLBACK_CANCEL; priv = data; elm_object_signal_emit(priv->base, SIG_SHOW_VIEW, ""); return ECORE_CALLBACK_CANCEL; } static void _show(void *view_data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; media_content_connect(); _mplayer_show(priv); _list_set_focus(priv); _player_play(priv); evas_object_show(priv->base); ecore_timer_add(SHOW_VIEW_INTERVAL, _show_view, priv); } static void _hide(void *view_data) { struct _priv *priv; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; _mplayer_hide(priv); media_content_disconnect(); evas_object_hide(priv->base); elm_object_signal_emit(priv->base, SIG_HIDE_VIEW, ""); } static void _update(void *view_data, int update_type, void *data) { struct _priv *priv; struct view_update_data *vdata; if (!view_data || !data) { _ERR("failed to get view data"); return; } priv = view_data; vdata = data; switch (update_type) { case UPDATE_CONTENT: if (priv->playlist.list_org != vdata->list) { priv->playlist.list_org = vdata->list; priv->playlist.list = eina_list_clone(vdata->list); _playlist_set_list(priv, priv->playlist.shuffle, vdata->index); } else { priv->playlist.cur = _playlist_get_index( priv->playlist.list, vdata->list, vdata->index); } priv->prev_view = vdata->id; _add_playlist_item(priv); break; case UPDATE_PLAYER: if (vdata->status == E_PLAYER_STOP) _player_stop(priv); /* NOTE: if need, adds play/pause */ 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; _player_stop(priv); } static void _destroy(void *view_data) { struct _priv *priv; struct controller *ctl; Evas_Object *obj; int i; if (!view_data) { _ERR("failed to get view data"); return; } priv = view_data; ctl = priv->ctl; for (i = 0; i < ARRAY_SIZE(btn_player); i++) { obj = ctl->ops->get_object(ctl->handle, i); if (obj) inputmgr_remove_callback(obj, &_btn_handler); } eina_list_free(priv->playlist.list); _list_free(priv); inputmgr_remove_callback(priv->list, &_list_handler); _player_stop(priv); playermgr_destroy(priv->player); controller_destroy(priv->ctl); progressbar_destroy(priv->progress); evas_object_del(priv->base); free(priv); } static view_class _vclass = { .view_id = VIEW_MPLAYER, .create = _create, .show = _show, .hide = _hide, .update = _update, .pause = _pause, .destroy = _destroy, }; view_class *view_mplayer_get_vclass(void) { return &_vclass; }