From d52f95790faec075078afa7418c7e01f83f425f5 Mon Sep 17 00:00:00 2001 From: "jinwoo.shin" Date: Mon, 29 Jun 2015 15:51:13 +0900 Subject: [PATCH 2/3] Initifal commit Change-Id: I4dd1bf891eff98d34f577d6384cc3c401733a220 Signed-off-by: jinwoo.shin --- CMakeLists.txt | 74 ++++++++++++++++++++ data/CMakeLists.txt | 32 +++++++++ data/live-tv-theme.edc | 20 ++++++ data/live-tv.edc | 33 +++++++++ include/define.h | 24 +++++++ include/main_view.h | 22 ++++++ org.tizen.live-tv.png | Bin 0 -> 38658 bytes org.tizen.live-tv.xml.in | 10 +++ packaging/org.tizen.live-tv.spec | 54 +++++++++++++++ src/main.c | 143 +++++++++++++++++++++++++++++++++++++++ src/main_view.c | 118 ++++++++++++++++++++++++++++++++ 11 files changed, 530 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 data/CMakeLists.txt create mode 100644 data/live-tv-theme.edc create mode 100644 data/live-tv.edc create mode 100644 include/define.h create mode 100644 include/main_view.h create mode 100644 org.tizen.live-tv.png create mode 100644 org.tizen.live-tv.xml.in create mode 100644 packaging/org.tizen.live-tv.spec create mode 100644 src/main.c create mode 100644 src/main_view.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bf712d2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,74 @@ +# 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. +# + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT("live-tv" C) + +IF(NOT DEFINED PACKAGE_NAME) + SET(PACKAGE_NAME "org.tizen.${PROJECT_NAME}") +ENDIF(NOT DEFINED PACKAGE_NAME) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +IF(NOT DEFINED BINDIR) + SET(BINDIR "${PREFIX}/bin") +ENDIF(NOT DEFINED BINDIR) +IF(NOT DEFINED RESDIR) + SET(RESDIR "${PREFIX}/res") +ENDIF(NOT DEFINED RESDIR) +IF(NOT DEFINED IMAGEDIR) + SET(IMAGEDIR "${PREFIX}/res/images") +ENDIF(NOT DEFINED IMAGEDIR) +IF(NOT DEFINED EDJEDIR) + SET(EDJEDIR "${PREFIX}/res/edje") +ENDIF(NOT DEFINED EDJEDIR) +IF(NOT DEFINED MANIFESTDIR) + SET(MANIFESTDIR "/usr/share/packages") +ENDIF(NOT DEFINED MANIFESTDIR) + +SET(SRCS src/main.c + src/main_view.c) + +SET(TARGET_EDJ "${PROJECT_NAME}.edj") +SET(THEME_EDJ "${PROJECT_NAME}-theme.edj") + +ADD_DEFINITIONS("-DPACKAGE=\"${PACKAGE_NAME}\"") +ADD_DEFINITIONS("-DEDJEDIR=\"${EDJEDIR}\"") +ADD_DEFINITIONS("-DIMAGEDIR=\"${IMAGEDIR}\"") +ADD_DEFINITIONS("-DEDJEFILE=\"${EDJEDIR}/${TARGET_EDJ}\"") +ADD_DEFINITIONS("-DTHEMEFILE=\"${EDJEDIR}/${THEME_EDJ}\"") + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) + +INCLUDE(FindPkgConfig) +pkg_check_modules(PKGS REQUIRED + elementary + ecore + edje + capi-appfw-application + app-utils) + +FOREACH(flag ${PKGS_CFLAGS}) + SET(EXTRA_CFLGAS "${EXTRA_CFLGAS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLGAS}") +ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${PKGS_LDFLAGS}) +CONFIGURE_FILE(${PACKAGE_NAME}.xml.in ${PACKAGE_NAME}.xml) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR}) +INSTALL(FILES ${PACKAGE_NAME}.xml DESTINATION ${MANIFESTDIR}) + +ADD_SUBDIRECTORY(data) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 0000000..c2bb8f9 --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,32 @@ +# 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. +# + +ADD_CUSTOM_TARGET(${TARGET_EDJ} + COMMAND edje_cc -id images + ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.edc + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_EDJ} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.edc +) +ADD_DEPENDENCIES(${PROJECT_NAME} ${TARGET_EDJ}) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_EDJ} DESTINATION ${EDJEDIR}) + +ADD_CUSTOM_TARGET(${THEME_EDJ} + COMMAND edje_cc -id images -sd sounds + ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}-theme.edc + ${CMAKE_CURRENT_BINARY_DIR}/${THEME_EDJ} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}-theme.edc +) +ADD_DEPENDENCIES(${PROJECT_NAME} ${THEME_EDJ}) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${THEME_EDJ} DESTINATION ${EDJEDIR}) diff --git a/data/live-tv-theme.edc b/data/live-tv-theme.edc new file mode 100644 index 0000000..52e787d --- /dev/null +++ b/data/live-tv-theme.edc @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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/define.h" + +collections { +} diff --git a/data/live-tv.edc b/data/live-tv.edc new file mode 100644 index 0000000..a1cfbd7 --- /dev/null +++ b/data/live-tv.edc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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/define.h" + +collections { + group { + name: GRP_MAIN_VIEW; + parts { + part { + name, PART_CONTENT; + type, RECT; + scale, 1; + description { + state, "default" 0.0; + } + } + } + } +} diff --git a/include/define.h b/include/define.h new file mode 100644 index 0000000..42bb0dd --- /dev/null +++ b/include/define.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __LIVETV_DEFINE_H__ +#define __LIVETV_DEFINE_H__ + +#define VIEW_MAIN "VIEW_MAIN" +#define GRP_MAIN_VIEW "grp.main.view" +#define PART_CONTENT "part.content" + +#endif /* __LIVETV_DEFINE_H__*/ diff --git a/include/main_view.h b/include/main_view.h new file mode 100644 index 0000000..6beb35d --- /dev/null +++ b/include/main_view.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef __LIVETV_MAIN_VIEW_H__ +#define __LIVETV_MAIN_VIEW_H__ + +view_class *view_main_get_vclass(void); + +#endif /* __LIVETV_MAIN_VIEW_H__*/ diff --git a/org.tizen.live-tv.png b/org.tizen.live-tv.png new file mode 100644 index 0000000000000000000000000000000000000000..affa43645cd9689ed9ea1d41404cfbb195a130ae GIT binary patch literal 38658 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�>=c|6It zB9{>TD4*!2)eY)(cou-Bc*u@7P{yUeRP_(RrcO{D);>waWxki8hxh`>13tVQlsZv- zPV53!S-lz|`xQHaS&eP#efVY1)x5PSOt%#q={PwhnJZ~<#kWy6E_bl^4@`SUf!C7% z?4{YykL|0xxu2N)tRq`X<+LC%fwRX=xe?MaC!8_7W0c zn4RZiy)Z0?2{R>}rFUEZOUw>?OM>xCVfKX31v>a= z;d}&HtXZJ>dzagM3`oS7a!j!B1%FhiPYL^2-}9!WLLCf4=AMmD$7hfOYQvZrMY)`k z3tcmF)u|w9~tGRgVtiwyrmd^^7~DvaT76IumC-JxlR7L6uf6 zN+(rmHd*7bsHQ1+9-%plE8p0K8NVo)*G~Unb1*pDL{N*JxM}J3jP?T-8*ij(V$o>l zH7tl%?&!w#HtN77=&|}-2<4ldAy>!JX$nzt9yYO-`W`LC^GFh6ppC4%O_zt|1w)n=}^FftB)F$xb7sG`!>$c zRRTUsWJ;fu3Le?ni_3TuG^nsgC8-mB;xr@+=iI;6Rb)s65q&{ftcg((RLmiD_7rxK z@6L2~%Wa^zmZvNt8$LBs=*D$5OrewRhDxUJ#T)I!c^sK2%{HM%m2TkyaK07$?H;bBA%<`lW;ZMhRy%oAFiSvGJo`otb335b2Q9}XQsBcaw zq!=Eo_5iR2n$v|W=M1CS*^i>2+O4fHb5Mtat{7X zwm2HWvhtVB8_^$R|L+Um?ehI2z9d@FP};sbkha`Ys41f<$-u@qaK(atJben`Nzcmj z#!`MlH!u#b+I>VLQH|AF)ivIMFgr=k(9a}a1UY%v*}OWas5gJhwWuM$;;s~#1FxZ> zm^rrZ8NEve#&^~>i9KR;TsqdPzN5YYkGE6VZtW3CaT52_=Hfk%lW?9Sp-Q@58&$>? z$0_6s368FK4lE(dZ_%I9_nbtujxSBtJRq{Ar3AO8v38ureZ437YQdYj)zOb^E@p+scaHSXN7|e*=`jdj zHHB!3|8#;CuNWscZTUn)HO~6}kR7^L&hI4s@BH^sA zj4&2M`$NK);M^^kRzyLsaT6@s7zzzV*I^NBwq;J4hL^(ta@O`-yk4;u1AOqSq&M!G zn5b=6Yx25$URd9_7Lmwf^6yk_4)*HoAz0$#4bXxe(p@ zrV6R~0~a_cv%94O=|K2?pi@qJyRCt8?l`(`a>NG=0{1(w0iJneW|qFY#%W|Ku#d1%eKwbY~aCmkkW#j(|A9HxkN3Of^E z$ZPaD;4(y&45^$N8e4kxQ~-iiQ>>&^9+;7yhtIm+S*EH&t-Znr?htA18yy{8tB+GP4Pky34Wh$Ab(&2kwjYrDM_J>#KS4N z1xOdsDASuBR#}poie_TlOK7G?33-V)y>;Yr!3Zk7rh7S7k?fJ0C>h-g6hs@@&>i%Y zPU3IdX6ari(Q~_#W#HbrlsK(Wvu4RVt#I=$voSgf(hTy&*ME~J*86V(}UmbFO-_4D&oDWD--o~ zxquW5{+x5J$yc>MO-7oy>w_gEgVvM_f@JXbK2)-R6<-^bU(ZTVpc(3;38h1?KNl@d z(4O>cm>kPy;K#hXZU|(z@U{W7w$R*EPgI9*Ims4LD6xZz&WhfZVlG?~*0BE?&N#D7 z!MZtJT!OuqN<<4(Su4J>wl+>#)~^(YJ5tU)Hzj6jPULF-lrFu?oeA`*mj_N({{VFv z&2$Av?(9x(sO)Z>Aw6?TJ6)y1{2Xdkw>`_*DQ@nM$@^880z)8qg3 z8)lwcB)E~|Yn@RTdc$cg)KQmJH>eF^iOSDBKTbb7V)ZO%^MR_>b#xK$vnJw8BOA-C z&ptYF;)FRrEaJGjSbJQs{oQa<>fgdYc>W*v-$Ryj|6}(b?!SgFUYt4};D5RQ9^U)< z$MFDv+x>_8U%;!bEdRIjzlZ-U_& + + + Jinwoo Shin + Live TV application for Tizen TV + + + @DESKTOP_ICON@ + + diff --git a/packaging/org.tizen.live-tv.spec b/packaging/org.tizen.live-tv.spec new file mode 100644 index 0000000..2b9626e --- /dev/null +++ b/packaging/org.tizen.live-tv.spec @@ -0,0 +1,54 @@ +Name: org.tizen.live-tv +Summary: Live TV application for Tizen TV +Version: 0.1 +Release: 1 +Group: Applications +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz + +BuildRequires: cmake +BuildRequires: pkgconfig(capi-appfw-application) +BuildRequires: pkgconfig(elementary) +BuildRequires: pkgconfig(ecore) +BuildRequires: pkgconfig(edje) +BuildRequires: pkgconfig(app-utils) +BuildRequires: gettext-devel +BuildRequires: edje-bin + +%define _pkgdir %{_prefix}/apps/%{name} +%define _bindir %{_pkgdir}/bin +%define _resdir %{_pkgdir}/res +%define _datadir %{_pkgdir}/data +%define _edjedir %{_resdir}/edje +%define _manifestdir %{_datarootdir}/packages + +%description +Live TV application for Tizen TV. + +%prep +%setup -q + +%build +cmake \ + -DCMAKE_INSTALL_PREFIX=%{_pkgdir} \ + -DPACKAGE_NAME=%{name} \ + -DBINDIR=%{_bindir} \ + -DEDJEDIR=%{_edjedir} \ + -DMANIFESTDIR=%{_manifestdir} \ + -DVERSION=%{version} + +make %{?jobs:-j%jobs} + +%install +%make_install +install --directory %{buildroot}/%{_datadir} + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root,-) +%{_bindir}/* +%{_resdir}/* +%{_datadir} +%{_manifestdir}/%{name}.xml diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..14c9672 --- /dev/null +++ b/src/main.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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 "define.h" +#include "main_view.h" + +SET_TAG(PACKAGE) + +struct _appdata { + const char *name; + Evas_Object *win; +}; + +static Evas_Object *_add_win(const char *name) +{ + Evas_Object *win; + + win = elm_win_add(NULL, name, ELM_WIN_BASIC); + if (!win) { + _ERR("failed to create win"); + return NULL; + } + elm_win_alpha_set(win, EINA_FALSE); + elm_win_focus_highlight_enabled_set(win, EINA_TRUE); + elm_win_focus_highlight_style_set(win, "invisible"); + + evas_object_show(win); + + return win; +} + +static void _pause(void *data) +{ +} + +static void _resume(void *data) +{ +} + +static bool _create(void *data) +{ + struct _appdata *ad; + Evas_Object *win; + + if (!data) { + _ERR("failed to get data"); + return false; + } + + ad = data; + + elm_theme_overlay_add(NULL, THEMEFILE); + + win = _add_win(ad->name); + if (!win) { + _ERR("failed to create win object"); + return false; + } + + if (!viewmgr_create(win)) { + _ERR("failed to initialize viewmgr"); + evas_object_del(win); + return false; + } + + viewmgr_add_view(view_main_get_vclass(), NULL); + + ad->win = win; + + return true; +} + +static void _terminate(void *data) +{ + struct _appdata *ad; + + if (!data) { + _ERR("failed to get data"); + return; + } + + ad = data; + + viewmgr_remove_view(VIEW_MAIN); + viewmgr_destroy(); + + if (ad->win) { + evas_object_del(ad->win); + ad->win = NULL; + } +} + +static void _control(app_control_h control, void *data) +{ + struct _appdata *ad; + + if (!data) { + _ERR("failed to get data"); + return; + } + + ad = data; + + if (ad->win) + elm_win_activate(ad->win); + + viewmgr_push_view(VIEW_MAIN); +} + +int main(int argc, char *argv[]) +{ + struct _appdata ad; + ui_app_lifecycle_callback_s cbs = { + .create = _create, + .terminate = _terminate, + .pause = _pause, + .resume = _resume, + .app_control = _control, + }; + + memset(&ad, 0x00, sizeof(ad)); + ad.name = PACKAGE; + + return ui_app_main(argc, argv, &cbs, &ad); +} diff --git a/src/main_view.c b/src/main_view.c new file mode 100644 index 0000000..a159cdf --- /dev/null +++ b/src/main_view.c @@ -0,0 +1,118 @@ +/* + * 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 "define.h" +#include "main_view.h" + +struct _priv { + Evas_Object *base; +}; + +static Evas_Object *_create(Evas_Object *win, void *data) +{ + struct _priv *priv; + Evas_Object *base; + + 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"); + free(priv); + return NULL; + } + elm_layout_file_set(base, EDJEFILE, GRP_MAIN_VIEW); + + evas_object_size_hint_weight_set(base, + EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, base); + + priv->base = base; + viewmgr_set_view_data(VIEW_MAIN, priv); + + return base; +} + +static void _show(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = (struct _priv *) view_data; + + evas_object_show(priv->base); +} + +static void _hide(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = (struct _priv *) view_data; + + evas_object_hide(priv->base); +} + +static void _destroy(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = (struct _priv *) view_data; + + evas_object_del(priv->base); + + free(priv); +} + +static view_class vclass = { + .view_id = VIEW_MAIN, + .create = _create, + .show = _show, + .hide = _hide, + .destroy = _destroy, +}; + +view_class *view_main_get_vclass(void) +{ + return &vclass; +} -- 2.7.4 From 5079c3495657d255a0182300435d4e7926f46094 Mon Sep 17 00:00:00 2001 From: "jinwoo.shin" Date: Wed, 1 Jul 2015 10:19:45 +0900 Subject: [PATCH 3/3] Add channelinfo view Change-Id: Id78b567acc97d49c7acbcec4690b4b71b8f84533 Signed-off-by: jinwoo.shin --- CMakeLists.txt | 11 +- data/live-tv.edc | 14 +- data/view/channelinfo.edc | 211 +++++ include/define.h | 27 +- include/strings.h | 23 + include/tv.h | 95 +++ include/tv_service.h | 281 +++++++ include/{main_view.h => view_channelinfo.h} | 8 +- packaging/org.tizen.live-tv.spec | 2 + res/images/ic_thumbnail_favorite_01.png | Bin 0 -> 1406 bytes res/images/ic_thumbnail_lock_01_foc.png | Bin 0 -> 1250 bytes res/images/ic_thumbnail_lock_01_nor.png | Bin 0 -> 1266 bytes src/main.c | 161 +++- src/main_view.c | 118 --- src/tv.c | 1105 +++++++++++++++++++++++++++ src/tv_service.c | 776 +++++++++++++++++++ src/view_channelinfo.c | 348 +++++++++ 17 files changed, 3029 insertions(+), 151 deletions(-) create mode 100644 data/view/channelinfo.edc create mode 100644 include/strings.h create mode 100644 include/tv.h create mode 100644 include/tv_service.h rename include/{main_view.h => view_channelinfo.h} (79%) create mode 100644 res/images/ic_thumbnail_favorite_01.png create mode 100644 res/images/ic_thumbnail_lock_01_foc.png create mode 100644 res/images/ic_thumbnail_lock_01_nor.png delete mode 100644 src/main_view.c create mode 100644 src/tv.c create mode 100644 src/tv_service.c create mode 100644 src/view_channelinfo.c diff --git a/CMakeLists.txt b/CMakeLists.txt index bf712d2..ca1a401 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ IF(NOT DEFINED RESDIR) SET(RESDIR "${PREFIX}/res") ENDIF(NOT DEFINED RESDIR) IF(NOT DEFINED IMAGEDIR) - SET(IMAGEDIR "${PREFIX}/res/images") + SET(IMAGEDIR "${PREFIX}/res/images/") ENDIF(NOT DEFINED IMAGEDIR) IF(NOT DEFINED EDJEDIR) SET(EDJEDIR "${PREFIX}/res/edje") @@ -38,7 +38,9 @@ IF(NOT DEFINED MANIFESTDIR) ENDIF(NOT DEFINED MANIFESTDIR) SET(SRCS src/main.c - src/main_view.c) + src/tv.c + src/view_channelinfo.c + src/tv_service.c) SET(TARGET_EDJ "${PROJECT_NAME}.edj") SET(THEME_EDJ "${PROJECT_NAME}-theme.edj") @@ -57,7 +59,9 @@ pkg_check_modules(PKGS REQUIRED ecore edje capi-appfw-application - app-utils) + app-utils + vconf + gobject-2.0) FOREACH(flag ${PKGS_CFLAGS}) SET(EXTRA_CFLGAS "${EXTRA_CFLGAS} ${flag}") @@ -70,5 +74,6 @@ CONFIGURE_FILE(${PACKAGE_NAME}.xml.in ${PACKAGE_NAME}.xml) INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR}) INSTALL(FILES ${PACKAGE_NAME}.xml DESTINATION ${MANIFESTDIR}) +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/res/images DESTINATION ${RESDIR}) ADD_SUBDIRECTORY(data) diff --git a/data/live-tv.edc b/data/live-tv.edc index a1cfbd7..26d27bf 100644 --- a/data/live-tv.edc +++ b/data/live-tv.edc @@ -17,17 +17,5 @@ #include "../include/define.h" collections { - group { - name: GRP_MAIN_VIEW; - parts { - part { - name, PART_CONTENT; - type, RECT; - scale, 1; - description { - state, "default" 0.0; - } - } - } - } + #include "view/channelinfo.edc" } diff --git a/data/view/channelinfo.edc b/data/view/channelinfo.edc new file mode 100644 index 0000000..c0e95ad --- /dev/null +++ b/data/view/channelinfo.edc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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/define.h" + +group { + name, GRP_VIEW_CHANNELINFO; + parts { + part { + name, "bg"; + type, RECT; + scale, 1; + description { + state, "default" 0.0; + rel1.relative, 1.0 0.5; + rel2.relative, 1.0 0.5; + min, 536 174; + align, 1.0 0.5; + fixed, 1 1; + } + description { + state, "focused" 0.0; + inherit, "default" 0.0; + color, COLOR_BG_FOCUSED; + } + } + part { + name, "padding.left_top"; + type, SPACER; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "bg"; + relative, 0.0 0.0; + } + rel2 { + to, "bg"; + relative, 0.0 0.0; + } + min, 28 26; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, PART_CHANNELINFO_CHANNEL; + type, TEXT; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "padding.left_top"; + relative, 1.0 1.0; + } + rel2 { + to, "padding.left_top"; + relative, 1.0 1.0; + } + text { + font, FONT_LIGHT; + size, 28; + align, 0.0 0.5; + } + color, COLOR_TEXT_NORMAL; + min, 344 32; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, "padding.title"; + type, SPACER; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, PART_CHANNELINFO_CHANNEL; + relative, 0.0 1.0; + } + rel2 { + to, PART_CHANNELINFO_CHANNEL; + relative, 0.0 1.0; + } + min, 0 6; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, PART_CHANNELINFO_TITLE; + type, TEXT; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "padding.title"; + relative, 0.0 1.0; + } + rel2 { + to, "padding.title"; + relative, 0.0 1.0; + } + text { + font, FONT_LIGHT; + size, 28; + align, 0.0 0.5; + } + color, COLOR_TEXT_NORMAL; + min, 484 32; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, "padding.time"; + type, SPACER; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, PART_CHANNELINFO_TITLE; + relative, 0.0 1.0; + } + rel2 { + to, PART_CHANNELINFO_TITLE; + relative, 0.0 1.0; + } + min, 0 24; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, PART_CHANNELINFO_TIME; + type, TEXT; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "padding.time"; + relative, 0.0 1.0; + } + rel2 { + to, "padding.time"; + relative, 0.0 1.0; + } + text { + font, FONT_LIGHT; + size, 28; + align, 0.0 0.5; + } + color, 112 112 112 255; + min, 484 28; + align, 0.0 0.0; + fixed, 1 1; + } + } + part { + name, "padding.right_top"; + type, SPACER; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "bg"; + relative, 1.0 0.0; + } + rel2 { + to, "bg"; + relative, 1.0 0.0; + } + min, 22 28; + align, 1.0 0.0; + fixed, 1 1; + } + } + part { + name, PART_CHANNELINFO_STATUS; + type, SWALLOW; + scale, 1; + description { + state, "default" 0.0; + rel1 { + to, "padding.right_top"; + relative, 0.0 1.0; + } + rel2 { + to, "padding.right_top"; + relative, 0.0 1.0; + } + min, 142 28; + align, 1.0 0.0; + fixed, 1 1; + } + } + } +} diff --git a/include/define.h b/include/define.h index 42bb0dd..e11b3f4 100644 --- a/include/define.h +++ b/include/define.h @@ -17,8 +17,31 @@ #ifndef __LIVETV_DEFINE_H__ #define __LIVETV_DEFINE_H__ -#define VIEW_MAIN "VIEW_MAIN" -#define GRP_MAIN_VIEW "grp.main.view" +#include "strings.h" + +#define KEY_SVCID "svcid" + +#define VIEW_CHANNELINFO "VIEW_CHANNELINFO" #define PART_CONTENT "part.content" +#define FONT_REGULAR "TizenSans" +#define FONT_LIGHT "TizenSans:style=Light" +#define FONT_BOLD "TizenSans:style=Bold" + +#define COLOR_BG_NORMAL 255 255 255 255 +#define COLOR_BG_FOCUSED 0 119 246 255 +#define COLOR_TEXT_NORMAL 51 51 51 255 +#define COLOR_TEXT_FOCUSED 255 255 255 255 + +#define IMG_LOCKED_NOR IMAGEDIR"ic_thumbnail_lock_01_nor.png" +#define IMG_LOCKED_FOC IMAGEDIR"ic_thumbnail_lock_01_foc.png" +#define IMG_FAVORITE_NOR IMAGEDIR"ic_thumbnail_favorite_01.png" +#define IMG_FAVORITE_FOC IMG_FAVORITE_NOR + +#define GRP_VIEW_CHANNELINFO "grp.view.channelinfo" +#define PART_CHANNELINFO_CHANNEL "part.channelinfo.channel" +#define PART_CHANNELINFO_TITLE "part.channelinfo.title" +#define PART_CHANNELINFO_TIME "part.channelinfo.time" +#define PART_CHANNELINFO_STATUS "part.channelinfo.status" + #endif /* __LIVETV_DEFINE_H__*/ diff --git a/include/strings.h b/include/strings.h new file mode 100644 index 0000000..026a670 --- /dev/null +++ b/include/strings.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __AIR_LIVETV_STRINGS_H__ +#define __AIR_LIVETV_STRINGS_H__ + +#define STR_NOTITLE "No Information" +#define STR_NOTIME "No Data" + +#endif /* __AIR_LIVETV_STRINGS_H__*/ diff --git a/include/tv.h b/include/tv.h new file mode 100644 index 0000000..f98574a --- /dev/null +++ b/include/tv.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __TV_H__ +#define __TV_H__ + +#include +#include + +#define CHANNEL_NAME_MAX 128 +#define PROG_TITLE_MAX 128 +#define PROG_DESCR_MAX 128 + +#define MAJOR_MAX 135 +#define MINOR_MAX 999 +#define EPG_PROGRAM_OFFSET 25000 + +struct tv_channel_info { + int service_id; + long channel_major; + long channel_minor; + unsigned int channel_type; + + char channel_name[CHANNEL_NAME_MAX]; + time_t start_time; + time_t end_time; + time_t duration; + + int locked; + int digital; + int favorite; + int remembered; + int tune_locked; +}; + +struct tv_program_info { + int service_id; + time_t start_time; + time_t end_time; + time_t duration; + time_t current_time; + char prog_title[PROG_TITLE_MAX]; + char prog_description[PROG_DESCR_MAX]; +}; + +struct tv_program_request { + void (*tv_program_cb)(Eina_List *program_list, void *user_data); + void *user_data; +}; + +int tv_create(void); +int tv_destroy(void); +int tv_pause(void); +int tv_resume(void); + +int tv_overlay_set(void *window_id); + +Eina_List *tv_channel_get_list(void); +Eina_List *tv_channel_search_by_number(long major, long minor); +void tv_channel_del_list(Eina_List *channel_list); +int tv_get_current_service_id(int *service_id); +int tv_channel_tune(void); +int tv_channel_direct_tune(long major, long minor); +int tv_channel_tune_with_service_id(int service_id); +int tv_channel_tune_locked_channel(int service_id, char *password); +int tv_channel_next(void); +int tv_channel_prev(void); +int tv_channel_set_favorite(int service_id, Eina_Bool flag); +int tv_channel_add_channel(int service_id); +int tv_channel_del_channel(int service_id); +int tv_channel_lock_channel(int service_id, char *password); +int tv_channel_unlock_channel(int service_id, char *password); +const struct tv_channel_info *tv_channel_get_info(void); +void tv_channel_del_info(const struct tv_channel_info *channel_info); + +int tv_epg_get_program(int service_id, struct tv_program_request *request); +int tv_epg_get_cache_program(int service_id, struct tv_program_request *request); +int tv_epg_get_program_list(int service_id, struct tv_program_request *request); + +void tv_signal_cb_set(void (*cb)(void *data, int is_signal), void *data); + +#endif /* __TV_H__ */ diff --git a/include/tv_service.h b/include/tv_service.h new file mode 100644 index 0000000..bae9260 --- /dev/null +++ b/include/tv_service.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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 + +#define TVS_EPG_CURRENT_TIME -1 +#define PROGRAM_NAME_MAX_LEN 25 +#define CHANNEL_FILTER_STRING_MAX_LEN 100 + +/** + * An enumeration. + * Enumeration for tv service error type + */ +typedef enum +{ + TVS_ERROR_OK = 0, /**< tv service error ok */ + TVS_ERROR_NO_FREE_MEMORY = -1, /**< tv service error no free memory */ + TVS_ERROR_HANDLE_UNINITIALIZED = -2, /**< tv service error handle uninitialized */ + TVS_ERROR_RPC_CALL_FAIL = -3, /**< tv service error rpc call fail */ + TVS_ERROR_INVALID_PARAMETER = -4, /**< tv service error invalid input parameter */ + TVS_ERROR_INVALID_VALUE = -5, /**< tv service error invalid input value */ + TVS_ERROR_NOT_AVAILABLE = -6, /**< tv service error not available */ + TVS_ERROR_SEQLITE_BUSY = -7, /**< tv service error SQLite busy */ + TVS_ERROR_PROCESS_FAIL = -8, /**< tv service error process error*/ + TVS_ERROR_DATABASE_FAIL = -9, /**< tv service error database query fail*/ + TVS_ERROR_AUTHENTICATE_FAIL = -10, /**< tv service error Authenticate fail, ex, lock password match fail*/ + TVS_ERROR_UNKNOWN = -255 /**< tv service error unknown */ +} TvServiceError; + +typedef enum +{ + TV_SERVICE_CHANNEL_MODE_UNDEFINED = -1, /* Unknown mode */ + TV_SERVICE_CHANNEL_MODE_ALL, /* ALL channel mode */ + TV_SERVICE_CHANNEL_MODE_DIGITAL, /* Digital channel mode */ + TV_SERVICE_CHANNEL_MODE_ANALOG, /* Analog channel mode */ + TV_SERVICE_CHANNEL_MODE_DIGITAL_ANALOG, /* Digital & Analog channel mode*/ + TV_SERVICE_CHANNEL_MODE_FAVORITE, /* Analog channel mode */ + TV_SERVICE_CHANNEL_MODE_ALL_DIGITAL_ANALOG /* Digital & Analog channel mode (include delete channel)*/ +} TvServiceChannelMode; + +/** + * An enumeration. + * Enumeration for channel list sort type. + */ +typedef enum +{ + CHANNEL_SORT_TYPE_MAJOR_MINOR_NUMBER = 0x0, + CHANNEL_SORT_TYPE_SERVICE_ID, + CHANNEL_SORT_TYPE_PROGRAM_NAME, + CHANNEL_SORT_TYPE_MAX +} TvServiceChannelSortType; + +typedef enum +{ + TV_SERVICE_LIVE_EVENT_BEGIN, /* live event tune start*/ + TV_SERVICE_LIVE_EVENT_TUNER_LOCK, /* live event tune lock result*/ + TV_SERVICE_LIVE_EVENT_CHANNEL_LOCK, /* live event channel lock(video and audio mute)*/ + TV_SERVICE_LIVE_EVENT_CHANNEL_UNLOCK, /* live event channel unlock*/ + TV_SERVICE_LIVE_EVENT_AUTO_DESTROY, /* live event auto destroy*/ + TV_SERVICE_LIVE_EVENT_RESOLUTION /* live event resolution*/ +} TvServiceLiveEvent; +typedef void (*TvServiceLiveCallback) (TvServiceLiveEvent event, gpointer user_data, const gpointer data); +typedef struct +{ + gint freq; /**the frequence*/ + gint mod; /**the modulation*/ + gint apid; /**the audio pid*/ + gint vpid; /**the videa pid*/ + gint ppid; /**the pmt pid*/ + gint pro_num; /**the program number*/ +} TvServiceLiveTuneInfo; + +typedef struct +{ + // TvServiceGenLive *proxy; /**the live proxy object*/ + guint service_handle; /**the live service handle*/ + gint antenna_type; /**antenna type*/ + gint service_id; /**service id*/ + gint unique; /**identify*/ + TvServiceLiveTuneInfo tune_info; /**tune info*/ + TvServiceLiveCallback call_back; /**the call back function*/ + gpointer user_data; /**the user_data*/ + gpointer window_id; /**the window id*/ +} TvServiceLiveData; + +typedef gpointer TvServiceLive; + +typedef void* TvServiceEpg; + +typedef enum +{ + TVS_EPG_CREATE = 0, + TVS_EPG_DESTROY, + TVS_EPG_GET_CURRENT_PROGRAM, + TVS_EPG_GET_PROGRAM_LIST, + TVS_EPG_AUTO_DESTROY, + TVS_EPG_DATA_UPDATED +} tvs_epg_event_e; + +typedef void (*TvServiceEpgCallback) (tvs_epg_event_e type, gpointer epg_data, gpointer user_data); + +typedef struct +{ + guint service_id; + guint event_id; + guint start_time; + guint etm_location; + guint length_in_seconds; + guint8 title_text[256]; + guint current_time; + guint8 extended_text[256]; +} TvServiceEpgEventData; + +typedef enum +{ + TVS_MODULATION_TYPE_UNKNOWN = -1, + TVS_MODULATION_TYPE_QPSK, + TVS_MODULATION_TYPE_16QAM, + TVS_MODULATION_TYPE_32QAM, + TVS_MODULATION_TYPE_64QAM, + TVS_MODULATION_TYPE_128QAM, + TVS_MODULATION_TYPE_256QAM, + TVS_MODULATION_TYPE_AUTO_QAM, + TVS_MODULATION_TYPE_8VSB, + TVS_MODULATION_TYPE_16VSB, + TVS_MODULATION_TYPE_8PSK, + TVS_MODULATION_TYPE_NTSC, + TVS_MODULATION_TYPE_PAL, + TVS_MODULATION_TYPE_MAX +} TvServiceModulationTpye; +typedef enum +{ + TV_SERVICE_ANTENNA_TYPE_UNDEFINED = -1, /* Unknown type */ + TV_SERVICE_ANTENNA_TYPE_AIR, /* Antenna air type */ + TV_SERVICE_ANTENNA_TYPE_CABLE, /* Antenna cable type */ + TV_SERVICE_ANTENNA_TYPE_ALL /*Antenna all type */ +} TvServiceAntenna; +typedef enum +{ + TVS_VIDEO_TYPE_UNKNOWN = -1, + TVS_VIDEO_TYPE_MPEG2 , + TVS_VIDEO_TYPE_H264, + TVS_VIDEO_TYPE_JPEG, + TVS_VIDEO_TYPE_AVS, + TVS_VIDEO_TYPE_MAX +} TvServiceVideoType; + +typedef enum +{ + TVS_AUDIO_TYPE_UNKNOWN = -1, + TVS_AUDIO_TYPE_AC3, + TVS_AUDIO_TYPE_MPEG2, + TVS_AUDIO_TYPE_AAC, + TVS_AUDIO_TYPE_HE_AAC, + TVS_AUDIO_TYPE_MAX +} TvServiceAudioType; +typedef struct +{ + guint service_id; + guint frequency; + guint service_type; + guint channel_type; + + gulong program_number; + gulong source_id; + gulong stream_id; + gulong pcr_id; + gulong major; + gulong minor; + gulong vpid; + gulong apid; + gulong pmt_pid; + + gboolean hide_guide; + gboolean hidden; + gboolean locked; + gboolean remembered; + gboolean save_by_psi; + gboolean digital; + gboolean favorite; + + gchar program_name[PROGRAM_NAME_MAX_LEN]; + + TvServiceModulationTpye modulation_type; + TvServiceAntenna antenna_type; + TvServiceVideoType video_type; + TvServiceAudioType audio_type; +} TvServiceChannel; + +typedef enum { + CHANNEL_FILTER_MATCH_NONE = 0, + CHANNEL_FILTER_MATCH_EQUAL, + CHANNEL_FILTER_MATCH_MORE, + CHANNEL_FILTER_MATCH_LESS, + CHANNEL_FILTER_MATCH_UNEQUAL, + CHANNEL_FILTER_MATCH_CONTAIN, + CHANNEL_FILTER_MATCH_MAX +} TvServiceChannelFilterMatchType; + +typedef enum { + TV_SERVICE_CHANNEL_DATA_NONE = 0, + TV_SERVICE_CHANNEL_DATA_SERVICE_ID, + TV_SERVICE_CHANNEL_DATA_FREQUENCY, + TV_SERVICE_CHANNEL_DATA_MAJOR_NUMBER, + TV_SERVICE_CHANNEL_DATA_MINOR_NUMBER, + TV_SERVICE_CHANNEL_DATA_PROGRAM_NAME, + TV_SERVICE_CHANNEL_DATA_REMEMBERED, + TV_SERVICE_CHANNEL_DATA_MAX +} TvServiceChannelDataAttr; + +typedef struct { + TvServiceChannelDataAttr attribute; + TvServiceChannelFilterMatchType match_type; + GValue *value; +} TvServiceFilterNode; + +typedef struct { + gint identity; + // TvServiceGenEpg *proxy; + TvServiceEpgCallback cb; + gboolean cb_flag; + gpointer user_data; + GList *event_list; +} EpgProxyData; + +typedef struct { + guint service_id; + guint start_time; + guint duration; + TvServiceEpgCallback event_callback; + gpointer event_callback_userdata; +} EpgProxyEventData; + +gint tv_service_live_get_service_id (TvServiceLive live, gint * service_id); +gint tv_service_get_channel (gint service_id, TvServiceChannel * channel); +gint tv_service_get_channel_list (TvServiceChannelMode mode, TvServiceAntenna type, GList ** channels); +gint tv_service_get_channel_list_ex (TvServiceChannelMode mode, TvServiceAntenna type, GList ** channels, + GList * filter, TvServiceChannelSortType sort_type); +gint tv_service_epg_get_current_program (TvServiceEpg epg, guint service_id, TvServiceEpgCallback callback_func, gpointer user_data); +gint tv_service_epg_get_cache_current_program (TvServiceEpg epg, guint service_id, TvServiceEpgEventData * app_data); +gint tv_service_epg_get_program_list (TvServiceEpg epg, guint service_id, guint start_time, guint duration, TvServiceEpgCallback callback_func, gpointer user_data); +gint tv_service_live_create (TvServiceLive * live); +gint tv_service_live_destroy (TvServiceLive live); +gint tv_service_live_tune_locked_channel (TvServiceLive live, gint service_id, gchar * password); +gint tv_service_epg_create (TvServiceEpg * epg); +gint tv_service_epg_destroy (TvServiceEpg epg); +gint tv_service_live_get_last_channel (gint * service_id); +gint tv_service_live_get_antenna_type (TvServiceLive live, TvServiceAntenna * type); +gint tv_service_live_tune (TvServiceLive live, gint service_id); +gint tv_service_lock_channel (gint service_id, gchar * password); +gint tv_service_unlock_channel (gint service_id, gchar * password); +gint tv_service_live_tune_down(TvServiceLive live, gint channel_mode, + TvServiceAntenna antenna_type); +gint tv_service_live_tune_up(TvServiceLive live, gint channel_mode, + TvServiceAntenna antenna_type); +gint tv_service_add_favorite_channel(gint service_id); +gint tv_service_delete_favorite_channel(gint service_id); +gint tv_service_add_channel(gint service_id); +gint tv_service_delete_channel(gint service_id); +gint tv_service_live_get_audio_mute(TvServiceLive live_svc, gboolean *mute); +gint tv_service_live_set_audio_mute(TvServiceLive live_svc, gboolean mute); +gint tv_service_live_get_volume(TvServiceLive live_svc, gdouble *vol); +gint tv_service_live_set_volume(TvServiceLive live_svc, gdouble vol); +gint tv_service_live_set_window_overlay(TvServiceLive live_svc, void *window_id); +gint tv_service_channel_info_create(); +gint tv_service_channel_info_destroy(); +gint tv_service_live_register_callback(TvServiceLive live_svc, + void *_tv_service_event_cb, void *some_other_variable); diff --git a/include/main_view.h b/include/view_channelinfo.h similarity index 79% rename from include/main_view.h rename to include/view_channelinfo.h index 6beb35d..f5b01e2 100644 --- a/include/main_view.h +++ b/include/view_channelinfo.h @@ -14,9 +14,9 @@ * limitations under the License. */ -#ifndef __LIVETV_MAIN_VIEW_H__ -#define __LIVETV_MAIN_VIEW_H__ +#ifndef __LIVETV_VIEW_CHANNELINFO_H__ +#define __LIVETV_VIEW_CHANNELINFO_H__ -view_class *view_main_get_vclass(void); +view_class *view_channelinfo_get_vclass(void); -#endif /* __LIVETV_MAIN_VIEW_H__*/ +#endif /* __LIVETV_VIEW_CHANNELINFO_H__*/ diff --git a/packaging/org.tizen.live-tv.spec b/packaging/org.tizen.live-tv.spec index 2b9626e..4b27b9c 100644 --- a/packaging/org.tizen.live-tv.spec +++ b/packaging/org.tizen.live-tv.spec @@ -12,6 +12,8 @@ BuildRequires: pkgconfig(elementary) BuildRequires: pkgconfig(ecore) BuildRequires: pkgconfig(edje) BuildRequires: pkgconfig(app-utils) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(gobject-2.0) BuildRequires: gettext-devel BuildRequires: edje-bin diff --git a/res/images/ic_thumbnail_favorite_01.png b/res/images/ic_thumbnail_favorite_01.png new file mode 100644 index 0000000000000000000000000000000000000000..6af72f0796304885c01b6ad6ec073de3a0aeea56 GIT binary patch literal 1406 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 b2_!Ilei|xffBFq4s1)*a^>bP0l+XkK%>B2e literal 0 HcmV?d00001 diff --git a/src/main.c b/src/main.c index 14c9672..4a9b31d 100644 --- a/src/main.c +++ b/src/main.c @@ -17,25 +17,68 @@ #include #include #include +#include #include #include "define.h" -#include "main_view.h" +#include "view_channelinfo.h" +#include "tv.h" + +#define KEY_MAX 256 SET_TAG(PACKAGE) struct _appdata { const char *name; Evas_Object *win; + + int is_signal; +}; + +struct key_map { + const char *view; + const char *key[KEY_MAX]; +}; + +static struct key_map g_kmap[] = { + { + VIEW_CHANNELINFO, + { + KEY_ENTER, KEY_ENTER_REMOTE, KEY_CHANNELUP, + KEY_CHANNELUP_REMOTE, KEY_CHANNELDOWN, KEY_CHANNELDOWN_REMOTE + } + }, +}; + +static void _key_up_cb(int id, void *data, Evas *e, Evas_Object *obj, + Evas_Event_Key_Up *ev) +{ + size_t i, j; + + for (i = 0; i < sizeof(g_kmap) / sizeof(*g_kmap); i++) { + j = 0; + while (g_kmap[i].key[j]) { + if (!strcmp(ev->keyname, g_kmap[i].key[j])) { + viewmgr_show_view(g_kmap[i].view); + viewmgr_update_view(g_kmap[i].view, ev); + return; + } + j++; + } + } +} + +static input_handler key_handler = { + .key_up = _key_up_cb, }; static Evas_Object *_add_win(const char *name) { - Evas_Object *win; + Evas_Object *win, *trans; win = elm_win_add(NULL, name, ELM_WIN_BASIC); if (!win) { - _ERR("failed to create win"); + _ERR("elm_win_add failed"); return NULL; } elm_win_alpha_set(win, EINA_FALSE); @@ -44,21 +87,93 @@ static Evas_Object *_add_win(const char *name) evas_object_show(win); + trans = evas_object_rectangle_add(evas_object_evas_get(win)); + if (!trans) { + _ERR("Create transparent layer failed"); + evas_object_del(win); + return NULL; + } + + /* for transparent layer */ + evas_object_color_set(trans, 0, 0, 0, 0); + evas_object_render_op_set(trans, EVAS_RENDER_COPY); + elm_win_resize_object_add(win, trans); + evas_object_size_hint_weight_set(trans, + EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(trans); + return win; } +int _set_tv_overlay(Evas_Object *win) +{ + int r; + Ecore_Wl_Window *wl_win; + + wl_win = elm_win_wl_window_get(win); + + r = tv_overlay_set(wl_win); + + return r; +} + +static void _tv_signal_cb(void *data, int is_signal) +{ + struct _appdata *ad; + + if (!data) { + _ERR("failed to get data"); + return; + } + + ad = data; + + if (ad->is_signal == is_signal) + return; + + ad->is_signal = is_signal; +} + static void _pause(void *data) { } static void _resume(void *data) { + struct _appdata *ad; + int r; + + if (!data) { + _ERR("failed to get data"); + return; + } + + ad = data; + + r = tv_resume(); + if (r < 0) { + _ERR("Resume tv service failed"); + return; + } else if (r > 0) { + r = tv_channel_tune(); + if (r < 0) { + _ERR("Tune channel failed"); + return; + } + } + + r = _set_tv_overlay(ad->win); + if (r < 0) { + _ERR("Set overlay failed"); + return; + } } static bool _create(void *data) { struct _appdata *ad; Evas_Object *win; + int r; if (!data) { _ERR("failed to get data"); @@ -75,16 +190,32 @@ static bool _create(void *data) return false; } + r = tv_create(); + if (r < 0) { + _ERR("Create TV failed"); + evas_object_del(win); + return false; + } + + r = _set_tv_overlay(ad->win); + if (r < 0) { + _ERR("Set overlay failed"); + evas_object_del(win); + return false; + } + if (!viewmgr_create(win)) { _ERR("failed to initialize viewmgr"); evas_object_del(win); return false; } - viewmgr_add_view(view_main_get_vclass(), NULL); + viewmgr_add_view(view_channelinfo_get_vclass(), NULL); ad->win = win; + tv_signal_cb_set(_tv_signal_cb, ad); + inputmgr_add_callback(ad->win, 0, &key_handler, NULL); return true; } @@ -99,7 +230,11 @@ static void _terminate(void *data) ad = data; - viewmgr_remove_view(VIEW_MAIN); + tv_destroy(); + + inputmgr_remove_callback(ad->win, &key_handler); + + viewmgr_remove_view(VIEW_CHANNELINFO); viewmgr_destroy(); if (ad->win) { @@ -110,19 +245,23 @@ static void _terminate(void *data) static void _control(app_control_h control, void *data) { - struct _appdata *ad; + char *svcid; + int r; if (!data) { _ERR("failed to get data"); return; } - ad = data; - - if (ad->win) - elm_win_activate(ad->win); + r = app_control_get_extra_data(control, KEY_SVCID, &svcid); + if (r == SERVICE_ERROR_NONE) { + tv_channel_tune_with_service_id(atoll(svcid)); + free(svcid); + } else { + tv_channel_tune(); + } - viewmgr_push_view(VIEW_MAIN); + viewmgr_show_view(VIEW_CHANNELINFO); } int main(int argc, char *argv[]) diff --git a/src/main_view.c b/src/main_view.c deleted file mode 100644 index a159cdf..0000000 --- a/src/main_view.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 "define.h" -#include "main_view.h" - -struct _priv { - Evas_Object *base; -}; - -static Evas_Object *_create(Evas_Object *win, void *data) -{ - struct _priv *priv; - Evas_Object *base; - - 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"); - free(priv); - return NULL; - } - elm_layout_file_set(base, EDJEFILE, GRP_MAIN_VIEW); - - evas_object_size_hint_weight_set(base, - EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - elm_win_resize_object_add(win, base); - - priv->base = base; - viewmgr_set_view_data(VIEW_MAIN, priv); - - return base; -} - -static void _show(void *view_data) -{ - struct _priv *priv; - - if (!view_data) { - _ERR("failed to get view data"); - return; - } - - priv = (struct _priv *) view_data; - - evas_object_show(priv->base); -} - -static void _hide(void *view_data) -{ - struct _priv *priv; - - if (!view_data) { - _ERR("failed to get view data"); - return; - } - - priv = (struct _priv *) view_data; - - evas_object_hide(priv->base); -} - -static void _destroy(void *view_data) -{ - struct _priv *priv; - - if (!view_data) { - _ERR("failed to get view data"); - return; - } - - priv = (struct _priv *) view_data; - - evas_object_del(priv->base); - - free(priv); -} - -static view_class vclass = { - .view_id = VIEW_MAIN, - .create = _create, - .show = _show, - .hide = _hide, - .destroy = _destroy, -}; - -view_class *view_main_get_vclass(void) -{ - return &vclass; -} diff --git a/src/tv.c b/src/tv.c new file mode 100644 index 0000000..abacbe9 --- /dev/null +++ b/src/tv.c @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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 "define.h" +#include "tv.h" +#include "tv_service.h" + +#define DEFAULT_SERVICE 1 + +/** + * The Storage structure to used by tv related functions and events. + */ +struct _tv_info { + /**< The handle to use tv service live api. */ + TvServiceLive live_svc; + /**< The handle to use tv service epg api. */ + TvServiceEpg epg_svc; + + /**< Stores service id if tune to locked channel was succeeded. */ + int viewing_locked_channel; + + /**< The function pointer to pass tv signal event */ + void (*signal_cb)(void *data, int is_signal); + /**< An Additional data to passed to tv signal event */ + void *signal_cb_data; +}; + +static struct _tv_info g_tv_info; + +/** + * Gets the tv_channel_info with supplied TvServiceChannel. + * + * Abstracts tv service data structure. + * + * @param channel The channel data defined by tv service + * @return Channel information, or NULL if fails + */ +static struct tv_channel_info *_tv_channel_get_info(TvServiceChannel *channel) +{ + struct tv_channel_info *channel_info = NULL; + + if (!channel) { + _ERR("failed to get channel"); + return NULL; + } + + channel_info = calloc(1, sizeof(*channel_info)); + if (!channel_info) { + _ERR("failed to calloc channel info"); + return NULL; + } + + channel_info->service_id = channel->service_id; + channel_info->channel_major = channel->major; + channel_info->channel_minor = channel->minor; + channel_info->channel_type = channel->channel_type; + channel_info->locked = channel->locked; + channel_info->digital = channel->digital; + channel_info->favorite = channel->favorite; + channel_info->remembered = channel->remembered; + strncpy(channel_info->channel_name, channel->program_name, + CHANNEL_NAME_MAX); + + if (channel->service_id == g_tv_info.viewing_locked_channel) + channel_info->tune_locked = EINA_TRUE; + + return channel_info; +} + +/** + * Frees the tv_channel_info. + * + * @param channel_info tv_channel_info pointer to be freed + */ +void tv_channel_del_info(const struct tv_channel_info *channel_info) +{ + if (!channel_info) { + _ERR("failed to get channel info"); + return; + } + + free((void *)channel_info); +} + +/** + * Gets current channel's TvServiceChannel data from tv service. + * + * @param channel The pointer to store TvServiceChannel + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +static int _tv_get_channel(TvServiceChannel *channel) +{ + gint svc_id; + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_get_service_id(g_tv_info.live_svc, &svc_id); + if (r < 0) { + _ERR("failed to get service id"); + return -1; + } + + r = tv_service_get_channel(svc_id, channel); + if (r < 0) { + _ERR("failed to get channel"); + return -1; + } + + return 0; +} + +/** + * Returns current channel's info. + * + * tv_channel_get_info retrieves current channel's information + * from tv service. + * + * @return Returns current channel info, or NULL if fails + */ +const struct tv_channel_info *tv_channel_get_info(void) +{ + const struct tv_channel_info *channel_info; + TvServiceChannel channel; + int r; + + r = _tv_get_channel(&channel); + if (r < 0) { + _ERR("failed to get channel"); + return NULL; + } + + channel_info = _tv_channel_get_info(&channel); + + return channel_info; +} + +/** + * Returns tv_program_info with supplied TvServiceEpgEventData. + * + * Abstracts tv service data structure. + * + * @param prog TvServiceEpgEventData passed from tv service + * @return Returns tv_program_info, or NULL if fails + */ +static struct tv_program_info *_tv_get_program_info(TvServiceEpgEventData *prog) +{ + struct tv_program_info *prog_info; + + prog_info = calloc(1, sizeof(*prog_info)); + if (!prog_info) { + _ERR("failed to calloc program info"); + return NULL; + } + + prog_info->service_id = prog->service_id; + prog_info->start_time = prog->start_time; + prog_info->end_time = prog->start_time + prog->length_in_seconds; + prog_info->duration = prog->length_in_seconds; + prog_info->current_time = prog->current_time; + strncpy(prog_info->prog_title, (char *)prog->title_text, + sizeof(prog_info->prog_title) - 1); + strncpy(prog_info->prog_description, (char *)prog->extended_text, + sizeof(prog_info->prog_description) - 1); + + return prog_info; +} + +/** + * Gets current channel's service id from tv service. + * + * @param service_id The pointer to store service id + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_get_current_service_id(int *service_id) +{ + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_get_service_id(g_tv_info.live_svc, service_id); + if (r != TVS_ERROR_OK) { + _ERR("failed to get current service info"); + return -1; + } + + return 0; +} + +/** + * Frees epg list. + * + * @param epg_list Eina_List to be freed + */ +static void _tv_epg_del_list(Eina_List *epg_list) +{ + struct tv_program_info *data; + + EINA_LIST_FREE(epg_list, data) + free(data); +} + +/** + * Callback function to get EPG program list from tv service. + * + * _tv_epg_event_cb is called from tv service when banner + * have requested to get program data. + * If this function is called, then derives program list from epg_list + * and calls banner's callback function which included in user_data. + * + * @param type Event type + * @param epg_list EPG program list + * @param user_data tv_program_request to handle request + */ +static void _tv_epg_event_cb(tvs_epg_event_e type, GList *epg_list, + void *user_data) +{ + int i; + Eina_List *list = NULL; + TvServiceEpgEventData *epg_data; + struct tv_program_info *prog_info; + struct tv_program_request *request; + + if (!user_data) { + _ERR("failed to get user_data"); + return; + } + + for (i = 0; i < g_list_length(epg_list); i++) { + epg_data = (TvServiceEpgEventData *) + g_list_nth_data(epg_list, i); + + if (!epg_data) + continue; + + prog_info = _tv_get_program_info(epg_data); + if (prog_info) + list = eina_list_append(list, prog_info); + } + + request = (struct tv_program_request *) user_data; + if (request->tv_program_cb) + request->tv_program_cb(list, request->user_data); + + if (list) + _tv_epg_del_list(list); + + free(request); +} + +/** + * Send a request to tv service to get a current program with supplied service_id. + * + * Beware that service_id should be current tuned channel. + * If not, calling this function may cause unexpected behavior. + * And note that get a EPG program is asynchronous operation. + * So tv_program_request should be supplied to get a result from tv service. + * + * @param service_id The channel id to get current program + * @param request The structure for return program data which contains function pointer + * and additional data for banner + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_epg_get_program(int service_id, struct tv_program_request *request) +{ + int r; + + if (!g_tv_info.epg_svc) { + _ERR("failed to get epg service"); + return -1; + } + + if (!request) { + _ERR("failed to get tv_program_request"); + return -1; + } + + r = tv_service_epg_get_current_program(g_tv_info.epg_svc, service_id, + (TvServiceEpgCallback) _tv_epg_event_cb, request); + if (r != TVS_ERROR_OK) { + _ERR("failed to get epg get current program : %d", service_id); + return -1; + } + + return 0; +} + +/** + * Gets a cached current program with supplied service_id from tv service. + * + * Note that this function is vaild only when tv service having a cached data + * on that channel. + * To have a cached data, the channel ever been tuned before calling + * this function. + * + * @param service_id The channel id to get current program + * @param request The structure for return program data which contains + * function pointer and additional data for banner + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_epg_get_cache_program(int service_id, struct tv_program_request *request) +{ + Eina_List *list = NULL; + TvServiceEpgEventData epg_data; + struct tv_program_info *prog_info; + + int r; + + if (!g_tv_info.epg_svc) { + _ERR("failed to get epg service"); + return -1; + } + + if (!request) { + _ERR("failed to get tv_program_request"); + return -1; + } + + r = tv_service_epg_get_cache_current_program( + g_tv_info.epg_svc, service_id, &epg_data); + if (r != TVS_ERROR_OK) { + _ERR("failed to get epg get cached current program : %d", + service_id); + return -1; + } + + prog_info = _tv_get_program_info(&epg_data); + if (prog_info) + list = eina_list_append(list, prog_info); + + if (request->tv_program_cb) + request->tv_program_cb(list, request->user_data); + + if (list) + _tv_epg_del_list(list); + + free(request); + + return 0; +} + +/** + * Sends a request to tv service to get programs with supplied service_id. + * + * Beware that service_id should be current tuned channel. + * If not, calling this function may cause unexpected behavior. + * And note that get a EPG program is asynchronous operation. + * So tv_program_request should be supplied to get a result from tv service. + * + * @param service_id The channel id to get current program + * @param request The structure for return program data which contains function pointer and additional data for banner + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_epg_get_program_list(int service_id, + struct tv_program_request *request) +{ + int r; + + if (!g_tv_info.epg_svc) { + _ERR("failed to get epg service"); + return -1; + } + + if (!request) { + _ERR("failed to get tv_program_request"); + return -1; + } + + r = tv_service_epg_get_program_list(g_tv_info.epg_svc, service_id, + TVS_EPG_CURRENT_TIME, EPG_PROGRAM_OFFSET, + (TvServiceEpgCallback) _tv_epg_event_cb, request); + if (r != TVS_ERROR_OK) { + _ERR("failed to get epg get current program"); + return -1; + } + + return 0; +} + +/** + * Frees the TvServiceFilterNode. + * + * @param data TvServiceFilterNode pointer to be freed + */ +static void _tv_channel_free_filter(gpointer data) +{ + TvServiceFilterNode *filter_node; + + if (!data) + return; + + filter_node = (TvServiceFilterNode *) data; + + g_value_unset(filter_node->value); + g_free(filter_node->value); + g_free(filter_node); +} + +/** + * Gets a available channel list. + * + * Note that deleted channels and service channels are excluded by default. + * + * @return Available channel list, or NULL if fails + */ +Eina_List *tv_channel_get_list() +{ + GList *tvs_list = NULL; + Eina_List *channel_list = NULL; + TvServiceChannel *tvs_data = NULL; + const struct tv_channel_info *channel_info; + int r, i; + + r = tv_service_get_channel_list( + TV_SERVICE_CHANNEL_MODE_DIGITAL_ANALOG, + TV_SERVICE_ANTENNA_TYPE_ALL, &tvs_list); + if (r != TVS_ERROR_OK) { + _ERR("failed to get channel list"); + return NULL; + } + + for (i = 0; i < g_list_length(tvs_list); i++) { + tvs_data = (TvServiceChannel *) g_list_nth_data(tvs_list, i); + if (tvs_data) { + channel_info = _tv_channel_get_info(tvs_data); + if (channel_info) + channel_list = eina_list_append(channel_list, + channel_info); + free(tvs_data); + } + } + + return channel_list; +} + +TvServiceFilterNode *_tv_channel_get_filter( + TvServiceChannelDataAttr attribute, int type, void *data) +{ + TvServiceFilterNode *filter; + GValue *value; + + filter = g_malloc0(sizeof(*filter)); + if (!filter) + return NULL; + + filter->attribute = attribute; + + value = g_malloc0(sizeof(GValue)); + if (!value) { + g_free(filter); + return NULL; + } + + switch (type) { + case G_TYPE_STRING: + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, data); + + filter->match_type = CHANNEL_FILTER_MATCH_CONTAIN; + break; + case G_TYPE_INT: + g_value_init(value, G_TYPE_INT); + g_value_set_int(value, (gint) data); + + filter->match_type = CHANNEL_FILTER_MATCH_EQUAL; + break; + } + filter->value = value; + + return filter; +} + +/** + * Search channels that are starts with supplied major and minor number. + * + * Note that deleted channels and service channels are excluded by default. + * + * @param major Major channel number to search + * @param minor Minor channel number to search + * @return Found channel list, or NULL if fails + */ +Eina_List *tv_channel_search_by_number(long major, long minor) +{ + char buf[CHANNEL_FILTER_STRING_MAX_LEN]; + GList *tvs_list = NULL, *filter = NULL; + TvServiceFilterNode *filter_node; + TvServiceChannel *tvs_data; + Eina_List *channel_list = NULL; + const struct tv_channel_info *channel_info; + int i, r; + + if (major > 0 && major < MAJOR_MAX) { + snprintf(buf, CHANNEL_FILTER_STRING_MAX_LEN, "%ld", major); + + filter_node = _tv_channel_get_filter( + TV_SERVICE_CHANNEL_DATA_MAJOR_NUMBER, + G_TYPE_STRING, buf); + if (filter_node) + filter = g_list_append(filter, (gpointer)filter_node); + } + + if (minor > 0 && minor < MINOR_MAX) { + snprintf(buf, CHANNEL_FILTER_STRING_MAX_LEN, "%ld", minor); + + filter_node = _tv_channel_get_filter( + TV_SERVICE_CHANNEL_DATA_MINOR_NUMBER, + G_TYPE_STRING, buf); + if (filter_node) + filter = g_list_append(filter, (gpointer)filter_node); + } + + if (!filter) { + _ERR("failed to get filter"); + return NULL; + } + + r = tv_service_get_channel_list_ex( + TV_SERVICE_CHANNEL_MODE_ALL_DIGITAL_ANALOG, + TV_SERVICE_ANTENNA_TYPE_ALL, &tvs_list, filter, 0); + if (r != TVS_ERROR_OK) { + _ERR("failed to get channel list"); + goto free; + } + + for (i = 0; i < g_list_length(tvs_list); i++) { + tvs_data = (TvServiceChannel *) g_list_nth_data(tvs_list, i); + if (!tvs_data) + continue; + + channel_info = _tv_channel_get_info(tvs_data); + if (channel_info) + channel_list = eina_list_append(channel_list, + channel_info); + free(tvs_data); + } + +free: + if (tvs_list) + g_list_free(tvs_list); + + g_list_foreach(filter, (GFunc) _tv_channel_free_filter, NULL); + g_list_free(filter); + + return channel_list; +} + +/** + * Frees the tv_channel_info. + * + * @param channel_list channel_list pointer to be freed + */ +void tv_channel_del_list(Eina_List *channel_list) +{ + struct tv_channel_info *data; + + EINA_LIST_FREE(channel_list, data) + free(data); +} + +/** + * Tunes to specific channel with service id. + * + * @param service_id The channel id + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_tune_with_service_id(int service_id) +{ + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_tune(g_tv_info.live_svc, service_id); + if (r != TVS_ERROR_OK) { + _ERR("failed to set service"); + return -1; + } + + g_tv_info.viewing_locked_channel = -1; + + return 0; +} + +/** + * Tunes to last viewed channel. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_tune(void) +{ + int service_id; + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_get_last_channel(&service_id); + if (r < 0) { + _ERR("failed to get current service id"); + service_id = DEFAULT_SERVICE; + } + + r = tv_service_live_tune(g_tv_info.live_svc, service_id); + if (r != TVS_ERROR_OK) { + _ERR("failed to set service"); + return -1; + } + + g_tv_info.viewing_locked_channel = -1; + + return 0; +} + +/** + * Tunes to specific channel with major and minor. + * + * @param service_id The channel id + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_direct_tune(long major, long minor) +{ + GList *tvs_list = NULL, *filter = NULL; + TvServiceFilterNode *filter_node; + TvServiceChannel *tvs_data; + int r; + + if (major > 0 && major < MAJOR_MAX) { + filter_node = _tv_channel_get_filter( + TV_SERVICE_CHANNEL_DATA_MAJOR_NUMBER, + G_TYPE_INT, (void *) major); + if (filter_node) + filter = g_list_append(filter, (gpointer)filter_node); + } + + if (minor > 0 && minor < MINOR_MAX) { + filter_node = _tv_channel_get_filter( + TV_SERVICE_CHANNEL_DATA_MINOR_NUMBER, + G_TYPE_INT, (void *) minor); + if (filter_node) + filter = g_list_append(filter, (gpointer)filter_node); + } + + if (!filter) { + _ERR("failed to get filter"); + return -1; + } + + r = tv_service_get_channel_list_ex( + TV_SERVICE_CHANNEL_MODE_ALL_DIGITAL_ANALOG, + TV_SERVICE_ANTENNA_TYPE_ALL, &tvs_list, filter, 0); + if (r != TVS_ERROR_OK) + goto free; + + tvs_data = (TvServiceChannel *) g_list_nth_data(tvs_list, 0); + if (tvs_data) + r = tv_channel_tune_with_service_id(tvs_data->service_id); + else { + _ERR("failed to get tvs_data"); + r = -1; + } + + if (r == TVS_ERROR_OK) + g_tv_info.viewing_locked_channel = -1; + + g_list_foreach(tvs_list, (GFunc) g_free, NULL); + g_list_free(tvs_list); + +free: + g_list_foreach(filter, (GFunc) _tv_channel_free_filter, NULL); + g_list_free(filter); + + return r; +} + +/** + * Tunes to locked channel. + * + * @param service_id The channel id + * @param password 4 digit password + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_tune_locked_channel(int service_id, char *password) +{ + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_tune_locked_channel(g_tv_info.live_svc, + service_id, password); + if (r != TVS_ERROR_OK) { + _ERR("failed to set service"); + return -1; + } + + g_tv_info.viewing_locked_channel = service_id; + + return 0; +} + +/** + * Tunes to next channel. + * + * Note that deleted channels and service channels will skipped. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_next(void) +{ + TvServiceAntenna antenna_type; + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_get_antenna_type(g_tv_info.live_svc, &antenna_type); + if (r < 0) { + _ERR("failed to get antenna type"); + return -1; + } + + r = tv_service_live_tune_up(g_tv_info.live_svc, + TV_SERVICE_CHANNEL_MODE_DIGITAL_ANALOG, antenna_type); + if (r < 0) { + _ERR("failed to tune up"); + return -1; + } + + g_tv_info.viewing_locked_channel = -1; + + return 0; +} + +/** + * Tunes to previous channel. + * + * Note that deleted channels and service channels will skipped. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_prev(void) +{ + TvServiceAntenna antenna_type; + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_get_antenna_type(g_tv_info.live_svc, &antenna_type); + if (r < 0) { + _ERR("failed to get antenna type"); + return -1; + } + + r = tv_service_live_tune_down(g_tv_info.live_svc, + TV_SERVICE_CHANNEL_MODE_DIGITAL_ANALOG, antenna_type); + if (r < 0) { + _ERR("failed to tune down"); + return -1; + } + + g_tv_info.viewing_locked_channel = -1; + + return 0; +} + +/** + * Sets the channel's favorite status. + * + * @param service_id The channel id + * @param flag The value to be set + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_set_favorite(int service_id, Eina_Bool flag) +{ + int r; + + if (flag) { + r = tv_service_add_favorite_channel(service_id); + if (r < 0) { + _ERR("failed to add favorite channel"); + return -1; + } + } else { + r = tv_service_delete_favorite_channel(service_id); + if (r < 0) { + _ERR("failed to delete favorite channel"); + return -1; + } + } + + return 0; +} + +/** + * Adds the channel. + * + * If channel is added, the channel will be included at + * tv_channel_next, tv_channel_prev and tv_channel_get_list. + * + * @param service_id The channel id + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_add_channel(int service_id) +{ + int r; + + r = tv_service_add_channel(service_id); + if (r < 0) { + _ERR("failed to add channel"); + return -1; + } + + return 0; +} + +/** + * Deletes the channel. + * + * If channel is deleted, the channel will be omitted at + * tv_channel_next, tv_channel_prev and tv_channel_get_list. + * + * @param service_id The channel id + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_del_channel(int service_id) +{ + int r; + + r = tv_service_delete_channel(service_id); + if (r < 0) { + _ERR("failed to delete channel"); + return -1; + } + + return 0; +} + +/** + * Locks the channel. + * + * @param service_id The channel id + * @param password 4 digits password + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_lock_channel(int service_id, char *password) +{ + int r; + + r = tv_service_lock_channel(service_id, password); + if (r < 0) { + _ERR("failed to lock channel"); + return -1; + } + + return 0; +} + +/** + * Unlocks the channel. + * + * @param service_id The channel id + * @param password 4 digits password + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_channel_unlock_channel(int service_id, char *password) +{ + int r; + + r = tv_service_unlock_channel(service_id, password); + if (r < 0) { + _ERR("failed to unlock channel"); + return -1; + } + + return 0; +} + +/** + * Callback function for receives tv service events. + * + * @param event Event type + * @param user_data Not in use + * @param data Event specific detailed data + */ +void _tv_service_event_cb(TvServiceLiveEvent event, + gpointer user_data, const gpointer data) +{ + gboolean *lock_status; + + switch (event) { + case TV_SERVICE_LIVE_EVENT_TUNER_LOCK: + if (!data) { + _ERR("failed to get data"); + break; + } + + lock_status = (gboolean *) data; + if (g_tv_info.signal_cb) + g_tv_info.signal_cb(g_tv_info.signal_cb_data, + *lock_status); + break; + case TV_SERVICE_LIVE_EVENT_AUTO_DESTROY: + g_tv_info.live_svc = NULL; + break; + case TV_SERVICE_LIVE_EVENT_RESOLUTION: + case TV_SERVICE_LIVE_EVENT_BEGIN: + case TV_SERVICE_LIVE_EVENT_CHANNEL_LOCK: + case TV_SERVICE_LIVE_EVENT_CHANNEL_UNLOCK: + break; + } +} + +/** + * Sets tv signal callback function. + * + * @param cb The function pointer to get callback + * @param data An Additional data to passed to callback + */ +void tv_signal_cb_set(void (*cb)(void *data, int is_signal), void *data) +{ + g_tv_info.signal_cb = cb; + g_tv_info.signal_cb_data = data; +} + +/** + * Sets window id for tv overlay. + * + * @param window_id The window id to overlay + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_overlay_set(void *window_id) +{ + int r; + + if (!g_tv_info.live_svc) { + _ERR("failed to get live service"); + return -1; + } + + r = tv_service_live_set_window_overlay(g_tv_info.live_svc, window_id); + if (r < 0) { + _ERR("failed to set overlay"); + return -1; + } + + return 0; +} + +/** + * Destory the tv service handles. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_destroy(void) +{ + int r; + + if (g_tv_info.live_svc) { + r = tv_service_live_destroy(g_tv_info.live_svc); + if (r != TVS_ERROR_OK) + _ERR("failed to destroy live service"); + g_tv_info.live_svc = NULL; + } + + r = tv_service_channel_info_destroy(); + if (r < 0) + _ERR("failed to destroy channel info service"); + + if (g_tv_info.epg_svc) { + r = tv_service_epg_destroy(g_tv_info.epg_svc); + if (r != 0) + _ERR("failed to destroy epg service"); + g_tv_info.epg_svc = NULL; + } + + return 0; +} + +/** + * Create the tv service handles. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_create(void) +{ + int r; + + r = tv_service_live_create(&g_tv_info.live_svc); + if (r != TVS_ERROR_OK) { + _ERR("failed to create live service"); + goto err; + } + + r = tv_service_live_register_callback(g_tv_info.live_svc, + _tv_service_event_cb, NULL); + if (r != TVS_ERROR_OK) { + _ERR("failed to register live callback"); + goto err; + } + + r = tv_service_channel_info_create(); + if (r != TVS_ERROR_OK) { + _ERR("failed to create channel info service"); + goto err; + } + + r = tv_service_epg_create(&g_tv_info.epg_svc); + if (r != TVS_ERROR_OK) { + _ERR("failed to create epg service"); + goto err; + } + + return 0; +err: + tv_destroy(); + return -1; +} + +/** + * Pause the tv service handles. + * + * @return If the operation was sucessful 0 is returned; otherwise negative value is returned + */ +int tv_pause(void) +{ + return 0; +} + +/** + * Resume the tv service handles. + * + * Live service could be destroyed by tv service while app is pausing. + * If live service is destroyed, then _tv_service_event_cb sets + * g_tv_info.live_svc to NULL. + * So if g_tv_info.live_svc is NULL, then recreates live service and returns 1. + * Therefore, if tv_resume returns 1, then app needs to be set overlay and tune. + * Or returns 0, then app just needs to set overlay. + * + * @return 0 if successful; 1 if live service was destroyed; otherwise negative value is returned + */ +int tv_resume(void) +{ + int r; + + if (g_tv_info.live_svc) + return 0; + + r = tv_service_live_create(&g_tv_info.live_svc); + if (r != TVS_ERROR_OK) { + _ERR("failed to create live service"); + goto err; + } + + r = tv_service_live_register_callback(g_tv_info.live_svc, + _tv_service_event_cb, NULL); + if (r != TVS_ERROR_OK) { + _ERR("failed to register live callback"); + goto err; + } + + return 1; + +err: + tv_destroy(); + return -1; +} diff --git a/src/tv_service.c b/src/tv_service.c new file mode 100644 index 0000000..ea18fe7 --- /dev/null +++ b/src/tv_service.c @@ -0,0 +1,776 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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 "tv.h" +#include "tv_service.h" + +#define DEFAULT_TIMER_INTERVAL 1.0 +#define TVS_VOLUME_MIN 0.0 +#define TVS_VOLUME_MAX 10.0 +#define TVS_PASSWORD "0000" + +#define VCONF_CHANNEL "db/menu/broadcasting/last_channel" + +struct dummy_tv_service_struct +{ + guint service_id; + TvServiceEpgCallback callback_func; + gpointer user_data; +}; + +struct _tv_service_info +{ + gdouble volume; + gboolean is_mute; +}; + +struct _tv_service_info tv_service_info; +GList *channel_list = NULL, *program_list = NULL; + +static void init_constant_channel_data(TvServiceChannel *channel) +{ + memset(channel, 0, sizeof(TvServiceChannel)); + + channel->frequency = 567000000; + channel->service_type = 2; + channel->channel_type = 0; + + channel->program_number = 1; + channel->source_id = 1; + // channel->stream_id = + channel->pcr_id = 17; + channel->minor = 1; + channel->vpid = 17; + // channel->apid = + channel->pmt_pid =16; + + channel->hide_guide = FALSE; + channel->hidden = FALSE; + channel->locked = FALSE; + channel->remembered = TRUE; + channel->save_by_psi = TRUE; + channel->digital = TRUE; + + channel->modulation_type = TVS_MODULATION_TYPE_8VSB; + channel->antenna_type = TV_SERVICE_ANTENNA_TYPE_CABLE; + channel->video_type = TVS_VIDEO_TYPE_MPEG2; + channel->audio_type = TVS_AUDIO_TYPE_AAC; +} + +/* Creates 3 programs for each channel (service_id) + * with one hour duration each. +*/ +static void init_program_data(int service_id, struct timeval tv) +{ + GList *epg_program_glist = NULL; + int i; + for (i = 0; i < 15; i++) + { + TvServiceEpgEventData *epg_data = NULL; + epg_data = (TvServiceEpgEventData *)g_malloc0 (sizeof(TvServiceEpgEventData)); + memset(epg_data, 0, sizeof(TvServiceEpgEventData)); + + epg_data->service_id = service_id; + // epg_data->event_id = + if (i == 0 && service_id % 2 == 0) + { + // Start first programs of some channels 30 minutes earlier than current time + // and substitute the difference with length + epg_data->start_time = tv.tv_sec - 30 * 60; + epg_data->length_in_seconds = 90 * 60; + } else { + epg_data->start_time = tv.tv_sec + i * 60 * 60; + epg_data->length_in_seconds = 60 * 60; + } + epg_data->etm_location= 1; + snprintf(epg_data->title_text, 255, "Awesome Program %d", i + 1); + snprintf(epg_data->extended_text, 255, "Description of Awesome Program %d", i + 1); + epg_data->current_time = tv.tv_sec; + epg_program_glist = g_list_append (epg_program_glist, epg_data); + } + program_list = g_list_append(program_list, (gpointer)epg_program_glist); +} + +static void init_data() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + int i; + for (i = 1; i < 6; i++) + { + TvServiceChannel *channel = NULL; + channel = (TvServiceChannel *)g_malloc0(sizeof(TvServiceChannel)); + init_constant_channel_data(channel); + channel->service_id = i; + channel->major = (5 + i); + channel->favorite = i % 2 == 0 ? TRUE : FALSE; + snprintf(channel->program_name, PROGRAM_NAME_MAX_LEN - 1, "Super Channel %d", i); + channel_list = g_list_append(channel_list, (gpointer)channel); + init_program_data(i, tv); + } + + tv_service_info.volume = TVS_VOLUME_MAX / 2; + tv_service_info.is_mute = FALSE; +} + +/* 9 */ +gint tv_service_live_get_service_id(TvServiceLive live, gint *service_id) +{ + TvServiceLiveData *live_proxy; + + if (!live || !service_id) + return TVS_ERROR_NOT_AVAILABLE; + + live_proxy = (TvServiceLiveData *)live; + *service_id = live_proxy->service_id; + + _DBG("service_id = %d", *service_id); + + return TVS_ERROR_OK; +} + +/* 10 */ +gint tv_service_get_channel(gint service_id, TvServiceChannel *channel) +{ + _DBG("service_id = %d", service_id); + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + TvServiceChannel *local_channel = NULL; + local_channel = (TvServiceChannel *)g_list_nth_data(channel_list, service_id - 1); + memcpy(channel, local_channel, sizeof(TvServiceChannel)); + + return TVS_ERROR_OK; +} + +/* We intentionally make a new copy of the channel_list, + * because live-tv frees the list internally + */ +TvServiceChannel *first_match_channel = NULL; +static gint _get_channel_list(TvServiceChannelMode channel_mode, TvServiceAntenna antenna_type, GList **channels, + GList *filter, TvServiceChannelSortType sort_type) +{ + GList *tmp_list = NULL; + TvServiceChannel *from, *to; + int i, size = g_list_length(channel_list); + if (filter == NULL) + { + // return the whole list + for (i = 0; i < size; i++) + { + from = g_list_nth_data(channel_list, i); + to = (TvServiceChannel *)malloc(sizeof(TvServiceChannel)); + memcpy(to, from, sizeof(TvServiceChannel)); + tmp_list = g_list_append(tmp_list, to); + } + *channels = tmp_list; + return TVS_ERROR_OK; + } + + TvServiceFilterNode *filter_node; + // FIXME: g_list_length(filter) is always 1, why? + filter_node = g_list_nth_data(filter, 0); + switch (filter_node->attribute) + { + case TV_SERVICE_CHANNEL_DATA_MAJOR_NUMBER: + case TV_SERVICE_CHANNEL_DATA_MINOR_NUMBER: + if (filter_node->match_type == CHANNEL_FILTER_MATCH_CONTAIN) + { + _DBG("CHANNEL_FILTER_MATCH_CONTAIN"); + gchar *str_value; + str_value = (gchar *)g_value_get_string((const GValue *)filter_node->value); + gulong gul_major_minor; + int length; + for (i = 0; i < size; i++) + { + from = g_list_nth_data(channel_list, i); + gul_major_minor = filter_node->attribute == TV_SERVICE_CHANNEL_DATA_MAJOR_NUMBER + ? from->major : from->minor; + length = snprintf(NULL, 0, "%lu", gul_major_minor); + char char_major_minor[length + 1]; + snprintf(char_major_minor, length + 1, "%lu", gul_major_minor); + if (strstr(char_major_minor, str_value)) + { + if (first_match_channel == NULL) + { + // reserve a copy for CHANNEL_FILTER_MATCH_EQUAL + first_match_channel = (TvServiceChannel *)malloc(sizeof(TvServiceChannel)); + memcpy(first_match_channel, from, sizeof(TvServiceChannel)); + } + to = (TvServiceChannel *)malloc(sizeof(TvServiceChannel)); + memcpy(to, from, sizeof(TvServiceChannel)); + tmp_list = g_list_append(tmp_list, to); + } + } + } + else + { + // FIXME: filter_node->value is always NULL at this point, why? + // gint *int_value; + // int_value = g_value_get_int(filter_node->value); + if (filter_node->match_type == CHANNEL_FILTER_MATCH_EQUAL) + { + _DBG("CHANNEL_FILTER_MATCH_EQUAL"); + if (first_match_channel) + { + tmp_list = g_list_append(tmp_list, first_match_channel); + first_match_channel = NULL; + } + } + else if (filter_node->match_type == CHANNEL_FILTER_MATCH_MORE) + { + _DBG("[NOT IMPLEMENTED YET] channel_filter_match_more"); + } + else if (filter_node->match_type == CHANNEL_FILTER_MATCH_LESS) + { + _DBG("[NOT IMPLEMENTED YET] channel_filter_match_less"); + } + else if (filter_node->match_type == CHANNEL_FILTER_MATCH_UNEQUAL) + { + _DBG("[NOT IMPLEMENTED YET] channel_filter_match_unequal"); + } + else + { + _ERR("match_type[%d] is out of range", filter_node->match_type); + return TVS_ERROR_PROCESS_FAIL; + } + } + break; + case TV_SERVICE_CHANNEL_DATA_SERVICE_ID: + case TV_SERVICE_CHANNEL_DATA_FREQUENCY: + case TV_SERVICE_CHANNEL_DATA_REMEMBERED: + case TV_SERVICE_CHANNEL_DATA_PROGRAM_NAME: + _DBG("case %d is not implemented yet", filter_node->attribute); + break; + default: + _ERR("attribute[%d] is out of range", filter_node->attribute); + return TVS_ERROR_PROCESS_FAIL; + } + + if (g_list_length(tmp_list) < 1) { + first_match_channel = NULL; + *channels = NULL; + return TVS_ERROR_PROCESS_FAIL; + } + *channels = tmp_list; + return TVS_ERROR_OK; +} + +gint tv_service_get_channel_list(TvServiceChannelMode mode, TvServiceAntenna antenna_type, GList **channels) +{ + _DBG(""); + return _get_channel_list(mode, antenna_type, channels, NULL, 0); +} + +gint tv_service_get_channel_list_ex(TvServiceChannelMode mode, TvServiceAntenna antenna_type, GList **channels, + GList *filter, TvServiceChannelSortType sort_type) +{ + _DBG(""); + return _get_channel_list(mode, antenna_type, channels, filter, sort_type); +} + +static Eina_Bool _tv_service_epg_get_current_program_cb(void *data) +{ + struct dummy_tv_service_struct *tvs_epg; + tvs_epg = data; + TvServiceEpgCallback callback_func = tvs_epg->callback_func; + callback_func(TVS_EPG_GET_CURRENT_PROGRAM, + g_list_nth_data(program_list, tvs_epg->service_id - 1), + tvs_epg->user_data); + free(tvs_epg); + + return ECORE_CALLBACK_CANCEL; +} + +/* 11 */ +gint tv_service_epg_get_current_program(TvServiceEpg epg, guint service_id, + TvServiceEpgCallback callback_func, gpointer user_data) +{ + _DBG("service_id = %d", service_id); + if (service_id < 1 || g_list_length(channel_list) < service_id) + { + return TVS_ERROR_NOT_AVAILABLE; + } + + struct dummy_tv_service_struct *tvs_epg; + tvs_epg = calloc(1, sizeof(*tvs_epg)); + tvs_epg->service_id = service_id; + tvs_epg->callback_func = callback_func; + tvs_epg->user_data = user_data; + ecore_timer_add(DEFAULT_TIMER_INTERVAL, _tv_service_epg_get_current_program_cb, tvs_epg); + + return TVS_ERROR_OK; +} + +gint tv_service_epg_get_cache_current_program(TvServiceEpg epg, guint service_id, + TvServiceEpgEventData *app_data) +{ + _DBG("service_id = %d", service_id); + if (service_id < 1 || g_list_length(channel_list) < service_id) + { + return TVS_ERROR_NOT_AVAILABLE; + } + struct timeval tv; + gettimeofday(&tv, NULL); + GList *prog_list = NULL; + prog_list = g_list_nth_data(program_list, service_id - 1); + TvServiceEpgEventData *epg_data = NULL; + gint result = TVS_ERROR_NOT_AVAILABLE; + int i; + for (i = 0; i < g_list_length(prog_list); i++) + { + epg_data = (TvServiceEpgEventData *)g_list_nth_data(prog_list, i); + if (epg_data == NULL) + { + continue; + } + if (epg_data->start_time <= tv.tv_sec && + epg_data->start_time + epg_data->length_in_seconds > tv.tv_sec) + { + app_data->service_id = epg_data->service_id; + app_data->event_id = epg_data->event_id; + app_data->start_time = epg_data->start_time; + app_data->etm_location = epg_data->etm_location; + app_data->length_in_seconds = epg_data->length_in_seconds; + g_stpcpy ((gchar *)app_data->title_text, (gchar *)epg_data->title_text); + g_stpcpy ((gchar *)app_data->extended_text, (gchar *)epg_data->extended_text); + app_data->current_time = tv.tv_sec; + result = TVS_ERROR_OK; + break; + } + } + + return result; +} + +gint tv_service_epg_get_program_list(TvServiceEpg epg, guint service_id, + guint start_time, guint duration, + TvServiceEpgCallback callback_func, gpointer user_data) +{ + _DBG("service_id = %d", service_id); + if (service_id < 1 || g_list_length(channel_list) < service_id) + { + return TVS_ERROR_NOT_AVAILABLE; + } + + struct dummy_tv_service_struct *tvs_epg; + tvs_epg = calloc(1, sizeof(*tvs_epg)); + tvs_epg->service_id = service_id; + tvs_epg->callback_func = callback_func; + tvs_epg->user_data = user_data; + ecore_timer_add(DEFAULT_TIMER_INTERVAL, _tv_service_epg_get_current_program_cb, tvs_epg); + + return TVS_ERROR_OK; +} + +/* 7 */ +gint tv_service_live_tune(TvServiceLive live, gint service_id) +{ + TvServiceLiveData *live_proxy; + + if (!live) + return TVS_ERROR_NOT_AVAILABLE; + + _DBG("service_id = %d", service_id); + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + live_proxy = (TvServiceLiveData *)live; + live_proxy->service_id = service_id; + + vconf_set_int(VCONF_CHANNEL, service_id); + + return TVS_ERROR_OK; +} + +static gboolean _check_lock_unlock_password(gchar *password) +{ + if (g_strcmp0(TVS_PASSWORD, password)) + { + return FALSE; + } + return TRUE; +} + +gint tv_service_live_tune_locked_channel(TvServiceLive live, gint service_id, gchar * password) +{ + if (!live) + return TVS_ERROR_HANDLE_UNINITIALIZED; + + if (!password) { + _ERR("Password is NULL"); + return TVS_ERROR_INVALID_PARAMETER; + } + + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + TvServiceChannel *channel = NULL; + channel = (TvServiceChannel *)g_list_nth_data(channel_list, service_id - 1); + if (!channel) + return TVS_ERROR_NOT_AVAILABLE; + + if (_check_lock_unlock_password(password)) + return TVS_ERROR_OK; + else + return TVS_ERROR_AUTHENTICATE_FAIL; +} + +gint tv_service_live_tune_up(TvServiceLive live, gint channel_mode, TvServiceAntenna antenna_type) +{ + TvServiceLiveData *live_proxy; + gint service_id; + + if (!live) + return TVS_ERROR_NOT_AVAILABLE; + + live_proxy = (TvServiceLiveData *)live; + service_id = (live_proxy->service_id) % g_list_length(channel_list) + 1; + + return tv_service_live_tune(live, service_id); +} + +gint tv_service_live_get_antenna_type(TvServiceLive live, TvServiceAntenna *type) +{ + _DBG(""); + TvServiceLiveData *live_proxy; + live_proxy = (TvServiceLiveData *)live; + *type = live_proxy->antenna_type; + + return TVS_ERROR_OK; +} + +/* 6 */ +gint tv_service_live_get_last_channel(gint *service_id) +{ + gint r = -1; + + r = vconf_get_int(VCONF_CHANNEL, service_id); + if (r < 0) + return TVS_ERROR_NOT_AVAILABLE; + + if ((*service_id) < 1) + return TVS_ERROR_NOT_AVAILABLE; + + return TVS_ERROR_OK; +} + +gint tv_service_live_tune_down(TvServiceLive live, gint channel_mode, + TvServiceAntenna antenna_type) +{ + TvServiceLiveData *live_proxy; + gint service_id; + + if (!live) + return TVS_ERROR_NOT_AVAILABLE; + + live_proxy = (TvServiceLiveData *)live; + service_id = live_proxy->service_id - 1; + if (service_id == 0) + service_id = g_list_length(channel_list); + + return tv_service_live_tune(live, service_id); +} + +gint tv_service_add_favorite_channel(gint service_id) +{ + TvServiceChannel *channel = NULL; + + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + channel = (TvServiceChannel *) g_list_nth_data(channel_list, service_id - 1); + if (!channel) + return TVS_ERROR_NOT_AVAILABLE; + + channel->favorite = TRUE; + + return TVS_ERROR_OK; +} + +gint tv_service_delete_favorite_channel(gint service_id) +{ + TvServiceChannel *channel = NULL; + + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + channel = (TvServiceChannel *) g_list_nth_data(channel_list, service_id - 1); + if (!channel) + return TVS_ERROR_NOT_AVAILABLE; + + channel->favorite = FALSE; + + return TVS_ERROR_OK; +} + +gint tv_service_add_channel(gint service_id) +{ + TvServiceChannel *channel = NULL; + + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + channel = (TvServiceChannel *) g_list_nth_data(channel_list, service_id - 1); + if (!channel) + return TVS_ERROR_NOT_AVAILABLE; + + channel->remembered = TRUE; + + return TVS_ERROR_OK; +} + +gint tv_service_delete_channel(gint service_id) +{ + TvServiceChannel *channel = NULL; + + if (service_id < 1 || g_list_length(channel_list) < service_id) + return TVS_ERROR_NOT_AVAILABLE; + + channel = (TvServiceChannel *) g_list_nth_data(channel_list, service_id - 1); + if (!channel) + return TVS_ERROR_NOT_AVAILABLE; + + channel->remembered = FALSE; + + return TVS_ERROR_OK; +} + +gint tv_service_lock_channel(gint service_id, gchar *password) +{ + if (!password) + { + _ERR("Password is NULL"); + return TVS_ERROR_INVALID_PARAMETER; + } + if (service_id < 1 || g_list_length(channel_list) < service_id) + { + return TVS_ERROR_NOT_AVAILABLE; + } + TvServiceChannel *channel = NULL; + channel = (TvServiceChannel *)g_list_nth_data(channel_list, service_id - 1); + if (!channel) + { + return TVS_ERROR_NOT_AVAILABLE; + } + if (channel->locked) + { + // if channel is already locked, simply return success code + return TVS_ERROR_OK; + } + + if (_check_lock_unlock_password(password)) + { + channel->locked = TRUE; + return TVS_ERROR_OK; + } + + return TVS_ERROR_AUTHENTICATE_FAIL; +} + +gint tv_service_unlock_channel(gint service_id, gchar *password) +{ + if (!password) + { + _ERR("Password is NULL"); + return TVS_ERROR_INVALID_PARAMETER; + } + if (service_id < 1 || g_list_length(channel_list) < service_id) + { + return TVS_ERROR_NOT_AVAILABLE; + } + TvServiceChannel *channel = NULL; + channel = (TvServiceChannel *)g_list_nth_data(channel_list, service_id - 1); + if (!channel) + { + return TVS_ERROR_NOT_AVAILABLE; + } + if (!channel->locked) + { + // if channel is already unlocked, simply return success code + return TVS_ERROR_OK; + } + + if (_check_lock_unlock_password(password)) + { + channel->locked = FALSE; + return TVS_ERROR_OK; + } + + return TVS_ERROR_AUTHENTICATE_FAIL; +} + +gint tv_service_live_get_audio_mute(TvServiceLive live_svc, gboolean *mute) +{ + if (!live_svc) + return TVS_ERROR_HANDLE_UNINITIALIZED; + + if (!mute) + return TVS_ERROR_INVALID_PARAMETER; + + *mute = tv_service_info.is_mute; + + return TVS_ERROR_OK; +} + +gint tv_service_live_set_audio_mute(TvServiceLive live_svc, gboolean mute) +{ + if (!live_svc) + return TVS_ERROR_HANDLE_UNINITIALIZED; + + if (mute != TRUE && mute != FALSE) + return TVS_ERROR_INVALID_VALUE; + + tv_service_info.is_mute = mute; + + return TVS_ERROR_OK; +} + +gint tv_service_live_get_volume(TvServiceLive live_svc, gdouble *vol) +{ + if (!live_svc) + return TVS_ERROR_HANDLE_UNINITIALIZED; + + if (!vol) + return TVS_ERROR_INVALID_PARAMETER; + + *vol = tv_service_info.volume; + + return TVS_ERROR_OK; +} + +gint tv_service_live_set_volume(TvServiceLive live_svc, gdouble vol) +{ + if (!live_svc) + return TVS_ERROR_HANDLE_UNINITIALIZED; + + if (vol < TVS_VOLUME_MIN || vol > TVS_VOLUME_MAX) + return TVS_ERROR_INVALID_VALUE; + + tv_service_info.is_mute = FALSE; + tv_service_info.volume = vol; + + return TVS_ERROR_OK; +} + +/* 5 */ +/* 8 */ +gint tv_service_live_set_window_overlay(TvServiceLive live_svc, void *window_id) +{ + _DBG(""); + return 0; +} + +/* 1 */ +gint tv_service_live_create(TvServiceLive *live) +{ + _DBG(""); + init_data(); + TvServiceLiveData *local = NULL; + local = (TvServiceLiveData*)g_malloc0 (sizeof (TvServiceLiveData)); + // local->service_handle = + local->antenna_type = TV_SERVICE_ANTENNA_TYPE_CABLE; + local->service_id = -1; + // local->unique = + + local->tune_info.freq = 567000000; + local->tune_info.mod = 7; + // local->tune_info.apid = + local->tune_info.vpid = 17; + local->tune_info.ppid = 17; + local->tune_info.pro_num = 1; + + // local->call_back = + // local->user_data = + // local->window_id = + + *live = (TvServiceLive)local; + + return TVS_ERROR_OK; +} + +void _free_program_data(gpointer data) +{ + GList *epg_program_glist; + epg_program_glist = (GList *) data; + if (epg_program_glist) + g_list_free_full(epg_program_glist, (GDestroyNotify)g_free); +} + +gint tv_service_live_destroy(TvServiceLive live) +{ + _DBG(""); + g_list_free_full(program_list, (GDestroyNotify) _free_program_data); + g_list_free_full(channel_list, (GDestroyNotify)g_free); + + TvServiceLiveData *live_proxy; + live_proxy = (TvServiceLiveData *)live; + g_free (live_proxy); + live_proxy = NULL; + + return TVS_ERROR_OK; +} + +/* 2 */ +gint tv_service_live_register_callback(TvServiceLive live_svc, + void *_tv_service_event_cb, void *some_other_variable) +{ + _DBG(""); + return 0; +} + +/* 3 */ +gint tv_service_channel_info_create() +{ + _DBG(""); + return 0; +} + +gint tv_service_channel_info_destroy() +{ + _DBG(""); + return 0; +} + +/* 4 */ +gint tv_service_epg_create(TvServiceEpg * epg) +{ + _DBG(""); + EpgProxyData *proxy_data = NULL; + proxy_data = (EpgProxyData *)g_malloc0 (sizeof (EpgProxyData)); + // proxy_data->identity = identity; + proxy_data->event_list = NULL; + proxy_data->cb_flag = FALSE; + *epg = proxy_data; + + return TVS_ERROR_OK; +} + +gint tv_service_epg_destroy(TvServiceEpg epg) +{ + _DBG(""); + EpgProxyData *proxy_data = (EpgProxyData *)epg; + // g_object_unref (proxy_data->proxy); + g_list_free_full (proxy_data->event_list, (GDestroyNotify)g_free); + g_free (proxy_data); + + return TVS_ERROR_OK; +} diff --git a/src/view_channelinfo.c b/src/view_channelinfo.c new file mode 100644 index 0000000..365a0f8 --- /dev/null +++ b/src/view_channelinfo.c @@ -0,0 +1,348 @@ +/* + * 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 "define.h" +#include "tv.h" +#include "view_channelinfo.h" + +#define HIDE_DUR 10.0 +#define BUF_MAX 128 +#define STATUS_BOX_PADDING 10 + +struct _priv { + Evas_Object *base; + Ecore_Timer *hide_timer; + const struct tv_channel_info *channel_info; +}; + +static int _get_program_time(char *buf, int buf_len, time_t start_time, + time_t end_time) +{ + struct tm tm; + int r; + + if (start_time >= end_time) { + _ERR("invalid time"); + return -1; + } + + localtime_r(&start_time, &tm); + r = strftime(buf, buf_len, "%I:%M %P -", &tm); + if (r <= 0) + return -1; + + localtime_r(&end_time, &tm); + r = strftime(buf + r, buf_len - r, " %I:%M %P", &tm); + if (r <= 0) + return -1; + + return 0; +} + +static Eina_Bool _hide_timer(void *data) +{ + struct _priv *priv; + + if (!data) + return ECORE_CALLBACK_CANCEL; + + priv = data; + + priv->hide_timer = NULL; + + viewmgr_hide_view(VIEW_CHANNELINFO); + + return ECORE_CALLBACK_CANCEL; +} + +static void _start_hide_timer(struct _priv *priv) +{ + if (!priv) { + _ERR("failed to get priv"); + return; + } + + if (priv->hide_timer) + ecore_timer_reset(priv->hide_timer); + else + priv->hide_timer = ecore_timer_add(HIDE_DUR, _hide_timer, priv); +} + +static void _add_icon(Evas_Object *box, const char *file) +{ + Evas_Object *ic; + + ic = elm_icon_add(box); + if (!ic) { + _ERR("failed to create icon"); + return; + } + + elm_image_file_set(ic, file, NULL); + elm_image_resizable_set(ic, EINA_FALSE, EINA_FALSE); + elm_image_no_scale_set(ic, EINA_TRUE); + evas_object_show(ic); + + elm_box_pack_end(box, ic); +} + +static void _pack_icon_box(struct _priv *priv, + const struct tv_channel_info *channel_info) +{ + Evas_Object *box; + + if (!priv) { + _ERR("failed to get priv"); + return; + } + + box = elm_box_add(priv->base); + if (!box) { + _ERR("failed to create box"); + return; + } + evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + elm_box_horizontal_set(box, EINA_TRUE); + elm_box_padding_set(box, STATUS_BOX_PADDING, 0); + elm_box_align_set(box, 1.0, 0.5); + elm_object_part_content_set(priv->base, PART_CHANNELINFO_STATUS, box); + + if (channel_info->locked) + _add_icon(box, IMG_LOCKED_NOR); + if (channel_info->favorite) + _add_icon(box, IMG_FAVORITE_NOR); +} + +static void _load_channel_text(struct _priv *priv, + const struct tv_channel_info *channel_info) +{ + char buf[BUF_MAX]; + + if (!priv || !channel_info) { + _ERR("failed to get priv and channel info"); + return; + } + + if (channel_info->channel_minor > 0 && + channel_info->channel_minor < MINOR_MAX) + snprintf(buf, sizeof(buf), "%lu-%lu %s", + channel_info->channel_major, + channel_info->channel_minor, + channel_info->channel_name); + else + snprintf(buf, sizeof(buf), "%lu %s", + channel_info->channel_major, + channel_info->channel_name); + + elm_object_part_text_set(priv->base, + PART_CHANNELINFO_CHANNEL, buf); + + elm_object_part_text_set(priv->base, + PART_CHANNELINFO_TITLE, + STR_NOTITLE); + elm_object_part_text_set(priv->base, + PART_CHANNELINFO_TIME, + STR_NOTIME); +} + +static void _load_program_info(struct _priv *priv, + const struct tv_program_info *prog) +{ + char buf[BUF_MAX]; + int r; + + if (!priv || !prog) { + _ERR("failed to get priv and program data"); + return; + } + + if (prog->start_time && prog->end_time) { + r = _get_program_time(buf, sizeof(buf), + prog->start_time, + prog->end_time); + if (!r) + elm_object_part_text_set(priv->base, + PART_CHANNELINFO_TIME, buf); + } + + if (strlen(prog->prog_title) > 0) + elm_object_part_text_set(priv->base, + PART_CHANNELINFO_TITLE, prog->prog_title); +} + +static void _tv_program_cb(Eina_List *prog_list, void *data) +{ + struct _priv *priv; + struct tv_program_info *prog = NULL; + + if (!data) { + _ERR("failed to get data"); + return; + } + + priv = data; + + prog = (struct tv_program_info *) eina_list_nth(prog_list, 0); + if (prog) { + if (priv->channel_info && + prog->service_id != priv->channel_info->service_id) + return; + else + _load_program_info(priv, prog); + } else { + _ERR("failed to get tv_program_info"); + } + + _start_hide_timer(priv); +} + +static void _update_channel_info(struct _priv *priv) +{ + struct tv_program_request *prog_req; + int r; + + if (!priv) { + _ERR("failed to get priv"); + return; + } + + if (priv->channel_info) { + tv_channel_del_info(priv->channel_info); + priv->channel_info = NULL; + } + + priv->channel_info = tv_channel_get_info(); + if (!priv->channel_info) { + _ERR("failed to get channel info"); + + return; + } + + if (priv->hide_timer) { + ecore_timer_del(priv->hide_timer); + priv->hide_timer = NULL; + } + + _load_channel_text(priv, priv->channel_info); + _pack_icon_box(priv, priv->channel_info); + + prog_req = calloc(1, sizeof(*prog_req)); + prog_req->tv_program_cb = _tv_program_cb; + prog_req->user_data = priv; + r = tv_epg_get_program(priv->channel_info->service_id, + prog_req); + if (r < 0) + _start_hide_timer(priv); +} + +static Evas_Object *_create(Evas_Object *win, void *data) +{ + struct _priv *priv; + Evas_Object *base; + + 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"); + free(priv); + return NULL; + } + elm_layout_file_set(base, EDJEFILE, GRP_VIEW_CHANNELINFO); + + evas_object_size_hint_weight_set(base, + EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, base); + + priv->base = base; + viewmgr_set_view_data(VIEW_CHANNELINFO, priv); + + return base; +} + +static void _show(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = view_data; + + _update_channel_info(priv); + + evas_object_show(priv->base); +} + +static void _hide(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = view_data; + + evas_object_hide(priv->base); +} + +static void _destroy(void *view_data) +{ + struct _priv *priv; + + if (!view_data) { + _ERR("failed to get view data"); + return; + } + + priv = view_data; + + evas_object_del(priv->base); + + free(priv); +} + +static view_class vclass = { + .view_id = VIEW_CHANNELINFO, + .create = _create, + .show = _show, + .hide = _hide, + .destroy = _destroy, +}; + +view_class *view_channelinfo_get_vclass(void) +{ + return &vclass; +} -- 2.7.4