diff options
author | Igor Nazarov <i.nazarov@samsung.com> | 2017-05-16 21:26:00 +0300 |
---|---|---|
committer | Igor Nazarov <i.nazarov@samsung.com> | 2017-05-18 13:38:19 +0300 |
commit | e0ddb963aeb9ec8eeaf2b27c3c61f6c73c9789b3 (patch) | |
tree | 39745a07881aef25ce016b9f27b45c7333d21e94 | |
parent | aa6a3b29c3d5fc23dcb19a52e8943e5a7bc596b0 (diff) | |
download | gallery-e0ddb963aeb9ec8eeaf2b27c3c61f6c73c9789b3.tar.gz gallery-e0ddb963aeb9ec8eeaf2b27c3c61f6c73c9789b3.tar.bz2 gallery-e0ddb963aeb9ec8eeaf2b27c3c61f6c73c9789b3.zip |
TizenRefApp-8516 [Gallery] Implement VideoPlayerPage video functionality
- Implemented VideoPlayerPage with video playback functionality (no sound
control);
- Added MIME parsing to MediaItem;
- VideoPlayerPage integrated to view operation;
- src/helpers.h ranamed to src/internal.h;
- Other minir changes and fixes.
Change-Id: I8bc057d6a38cf89ca82a1c100c865af3b855ba7e
-rw-r--r-- | inc/presenters/Instance.h | 5 | ||||
-rw-r--r-- | inc/presenters/Presenter.h | 6 | ||||
-rw-r--r-- | inc/presenters/VideoPlayerPage.h | 120 | ||||
-rw-r--r-- | inc/presenters/types.h | 1 | ||||
-rw-r--r-- | src/common.h | 2 | ||||
-rw-r--r-- | src/internal.h (renamed from src/helpers.h) | 11 | ||||
-rw-r--r-- | src/internal.hpp (renamed from src/helpers.hpp) | 14 | ||||
-rw-r--r-- | src/model/MediaItem.cpp | 28 | ||||
-rw-r--r-- | src/model/common.h | 2 | ||||
-rw-r--r-- | src/model/internal.cpp (renamed from src/model/helpers.cpp) | 6 | ||||
-rw-r--r-- | src/model/internal.h (renamed from src/model/helpers.h) | 10 | ||||
-rw-r--r-- | src/model/internal.hpp (renamed from src/model/helpers.hpp) | 8 | ||||
-rw-r--r-- | src/presenters/AlertDialog.cpp | 3 | ||||
-rw-r--r-- | src/presenters/Instance.cpp | 65 | ||||
-rw-r--r-- | src/presenters/MoreOptionsPresenter.cpp | 2 | ||||
-rw-r--r-- | src/presenters/Presenter.cpp | 19 | ||||
-rw-r--r-- | src/presenters/VideoPlayerPage.cpp | 589 | ||||
-rw-r--r-- | src/presenters/ViewerPage.cpp | 3 | ||||
-rw-r--r-- | src/presenters/common.h | 2 | ||||
-rw-r--r-- | src/presenters/internal.cpp | 43 | ||||
-rw-r--r-- | src/presenters/internal.h | 31 | ||||
-rw-r--r-- | tizen-manifest.xml | 1 |
22 files changed, 931 insertions, 40 deletions
diff --git a/inc/presenters/Instance.h b/inc/presenters/Instance.h index 8c32671..a9c949a 100644 --- a/inc/presenters/Instance.h +++ b/inc/presenters/Instance.h @@ -60,9 +60,12 @@ namespace gallery { ucl::Result handleGroupMode(const std::string &operation, app_control_h appControl); + ucl::Result ensureGalleryModel(); + void createNoContentPage(); void createThumbnailPage(); - void createViewerPage(std::string filePath); + void createViewerPage(const MediaItemSRef &media); + void createVideoPlayerPage(const MediaItemSRef &media); void onAlbumChanged(); void onPageExitRequest(Page &page); diff --git a/inc/presenters/Presenter.h b/inc/presenters/Presenter.h index 3400cff..2366f5b 100644 --- a/inc/presenters/Presenter.h +++ b/inc/presenters/Presenter.h @@ -19,7 +19,7 @@ #include <unordered_set> -#include "ucl/gui/ElmWidget.h" +#include "ucl/gui/Window.h" #include "types.h" @@ -45,6 +45,8 @@ namespace gallery { ucl::Result prepare(ucl::ElmWidget &widget); + ucl::Window &getWindow(); + void addDeactivatorSource(ucl::Widget &source); void addDeactivatorException(void *deactivator); @@ -76,7 +78,7 @@ namespace gallery { private: std::unordered_set<void *> m_deactivatorExceptions; std::unordered_set<void *> m_deactivators; - ucl::WidgetWRef m_topWidget; + ucl::WindowSRef m_window; bool m_isPrepared; }; } diff --git a/inc/presenters/VideoPlayerPage.h b/inc/presenters/VideoPlayerPage.h new file mode 100644 index 0000000..97ea5c7 --- /dev/null +++ b/inc/presenters/VideoPlayerPage.h @@ -0,0 +1,120 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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 __GALLERY_PRESENTERS_VIDEO_PLAYER_PAGE_H__ +#define __GALLERY_PRESENTERS_VIDEO_PLAYER_PAGE_H__ + +#include <player.h> + +#include "ucl/gui/Layout.h" + +#include "Page.h" + +namespace gallery { + + class VideoPlayerPage final : public Page { + public: + class Builder { + public: + Builder(); + ~Builder(); + Builder &setNaviframe(const ucl::NaviframeSRef &navi); + Builder &setMedia(const MediaItemSRef &media); + VideoPlayerPageWRef build(ExitRequestHandler onExitRequest) const; + private: + ucl::NaviframeSRef m_navi; + MediaItemSRef m_media; + }; + + private: + friend class ucl::RefCountObj<VideoPlayerPage>; + VideoPlayerPage(ucl::RefCountObjBase &rc, + const ucl::NaviframeSRef &navi, + ExitRequestHandler onExitRequest, + const MediaItemSRef &media); + virtual ~VideoPlayerPage(); + + ucl::Result prepare(); + + void createImage(); + ucl::Result preparePlayer(); + ucl::Result seekToStart(); + + void createControls(); + void createButton(ucl::ElmStyle style, ucl::EdjePart part, + const ucl::WidgetEventHandler &handler); + + bool resetTimer(Ecore_Timer *&timer, double timeout, Ecore_Task_Cb func); + void stopTimer(Ecore_Timer *&timer); + + bool resetAutoStartTimer(); + bool resetControlsHideTimer(); + bool resetTickTimer(); + + void showControls(); + void hideControls(); + + bool updatePlayTimeText(); + void updatePlayTimeText(int timeMs); + + player_state_e getPlayerState() const; + void startPlayback(); + void pausePlayback(); + + void setScreenOlwaysOn(bool isAlwaysOn); + + void onPlaybackComplete(); + void onPlaybackInterrupted(player_interrupted_code_e code); + void onSeekComplete(); + + Eina_Bool onAutoStartTimer(); + Eina_Bool onControlsHideTimer(); + Eina_Bool onTickTimer(); + + void onVolumeOnBtnClick(ucl::Widget &sender, void *eventInfo); + void onVolumeMuteBtnClick(ucl::Widget &sender, void *eventInfo); + void onPlayBtnClick(ucl::Widget &sender, void *eventInfo); + void onPauseBtnClick(ucl::Widget &sender, void *eventInfo); + void onTap(int x, int y); + Eina_Bool onRotary(Eext_Rotary_Event_Info *info); + + void onInstancePaused(ucl::Widget &sender, void *eventInfo); + void onInstanceResumed(ucl::Widget &sender, void *eventInfo); + + private: + enum class State { + PAUSED, + PLAYING + }; + + private: + const MediaItemSRef m_media; + ucl::LayoutSRef m_content; + ucl::WidgetSRef m_image; + TouchParserSRef m_touchParser; + player_h m_player; + int m_videoDuration; + Ecore_Timer *m_autoStartTimer; + Ecore_Timer *m_controlsHideTimer; + Ecore_Timer *m_tickTimer; + State m_state; + bool m_isControlsVisible; + bool m_needAutoStart; + bool m_isPlaybackCompleted; + }; +} + +#endif // __GALLERY_PRESENTERS_VIDEO_PLAYER_PAGE_H__ diff --git a/inc/presenters/types.h b/inc/presenters/types.h index b7fdb1a..2cc1e31 100644 --- a/inc/presenters/types.h +++ b/inc/presenters/types.h @@ -52,6 +52,7 @@ namespace gallery { UCL_DECLARE_REF_ALIASES(ThumbnailPage); UCL_DECLARE_REF_ALIASES(PreviewPage); UCL_DECLARE_REF_ALIASES(ViewerPage); + UCL_DECLARE_REF_ALIASES(VideoPlayerPage); } #endif // __GALLERY_PRESENTERS_TYPES_H__ diff --git a/src/common.h b/src/common.h index 15485db..696be60 100644 --- a/src/common.h +++ b/src/common.h @@ -26,6 +26,6 @@ #undef UCL_LOG_TAG #define UCL_LOG_TAG GALLERY_LOG_TAG -#include "helpers.h" +#include "internal.h" #endif // __GALLERY_COMMON_H__ diff --git a/src/helpers.h b/src/internal.h index 8cd1b3a..504f985 100644 --- a/src/helpers.h +++ b/src/internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __GALLERY_HELPERS_H__ -#define __GALLERY_HELPERS_H__ +#ifndef __GALLERY_INTERNAL_H__ +#define __GALLERY_INTERNAL_H__ #include "types.h" @@ -26,8 +26,11 @@ namespace gallery { namespace util { template <class GETTER, class V, class ...ARGS> ucl::Result getNz(GETTER &&getter, V &result, ARGS &&...args); + + template <class FUNC, class ...ARGS> + ucl::Result call(FUNC &&func, ARGS &&...args); }} -#include "helpers.hpp" +#include "internal.hpp" -#endif // __GALLERY_HELPERS_H__ +#endif // __GALLERY_INTERNAL_H__ diff --git a/src/helpers.hpp b/src/internal.hpp index 6acb919..343f37c 100644 --- a/src/helpers.hpp +++ b/src/internal.hpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "ucl/util/helpers.h" #include "ucl/util/logging.h" namespace gallery { namespace util { namespace himpl { @@ -24,7 +25,7 @@ namespace gallery { namespace util { namespace himpl { { char *value = nullptr; const int ret = getter(std::forward<ARGS>(args)..., &value); - if ((ret != 0) || (!optional && !value)) { + if ((ret != 0) || (!optional && ucl::isEmpty(value))) { UCL_ELOG("get() failed: %d", ret); return ucl::RES_FAIL; } @@ -71,4 +72,15 @@ namespace gallery { namespace util { return himpl::get(std::forward<GETTER>(getter), false, result, std::forward<ARGS>(args)...); } + + template <class FUNC, class ...ARGS> + inline ucl::Result call(FUNC &&func, ARGS &&...args) + { + const int ret = func(std::forward<ARGS>(args)...); + if (ret != 0) { + UCL_ELOG("func() failed: %d", ret); + return ucl::RES_FAIL; + } + return ucl::RES_OK; + } }} diff --git a/src/model/MediaItem.cpp b/src/model/MediaItem.cpp index b87ba31..4c42d84 100644 --- a/src/model/MediaItem.cpp +++ b/src/model/MediaItem.cpp @@ -18,11 +18,37 @@ #include <Ecore_File.h> #include <storage.h> +#include <mime_type.h> #include "BaseJob.h" #include "common.h" +namespace gallery { namespace { namespace impl { + + const std::string MIME_PREFIX_IMAGE {"image/"}; + const std::string MIME_PREFIX_VIDEO {"video/"}; + + MediaType getFileMediaType(const std::string &filePath) + { + const auto ext = util::extractFileExtension(filePath); + + std::string mime; + FAIL_RETURN_VALUE(util::get(mime_type_get_mime_type, mime, ext.c_str()), + MediaType::OTHERS, + "mime_type_get_mime_type() failed!"); + + if (util::beginsWith(mime, MIME_PREFIX_IMAGE)) { + return MediaType::IMAGE; + } + if (util::beginsWith(mime, MIME_PREFIX_VIDEO)) { + return MediaType::VIDEO; + } + + return MediaType::OTHERS; + } +}}} + namespace gallery { using namespace ucl; @@ -175,7 +201,7 @@ namespace gallery { MediaItemSRef MediaItem::newInstance(std::string filePath) { auto result = makeShared<MediaItem>(FLAGS_SIMPLE_FILE, - MediaType::IMAGE); + impl::getFileMediaType(filePath)); FAIL_RETURN_VALUE(result->prepare(std::move(filePath)), {}, "result->prepare() failed!"); diff --git a/src/model/common.h b/src/model/common.h index 7581a14..c1e6306 100644 --- a/src/model/common.h +++ b/src/model/common.h @@ -17,7 +17,7 @@ #ifndef __GALLERY_MODEL_COMMON_H__ #define __GALLERY_MODEL_COMMON_H__ -#include "helpers.h" +#include "internal.h" #include "../common.h" diff --git a/src/model/helpers.cpp b/src/model/internal.cpp index 4057ff5..de6f9a2 100644 --- a/src/model/helpers.cpp +++ b/src/model/internal.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "helpers.h" +#include "internal.h" #include <Ecore_File.h> #include <storage.h> @@ -83,7 +83,7 @@ namespace gallery { namespace util { return path.substr(bsPos + 1); } - std::string extractExtension(const std::string &name) + std::string extractFileExtension(const std::string &name) { const auto dotPos = name.rfind('.'); if ((dotPos == std::string::npos) || @@ -110,7 +110,7 @@ namespace gallery { namespace util { { baseName = extractFileName(path); if (isNotEmpty(baseName)) { - extension = extractExtension(baseName); + extension = extractFileExtension(baseName); if (isNotEmpty(extension)) { baseName.resize(baseName.size() - extension.size() - 1); } diff --git a/src/model/helpers.h b/src/model/internal.h index 29da4d8..415a58c 100644 --- a/src/model/helpers.h +++ b/src/model/internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __GALLERY_MODEL_HELPERS_H__ -#define __GALLERY_MODEL_HELPERS_H__ +#ifndef __GALLERY_MODEL_INTERNAL_H__ +#define __GALLERY_MODEL_INTERNAL_H__ #include "ucl/util/threading.h" @@ -44,8 +44,10 @@ namespace gallery { namespace util { std::string makeUniqueFilePath(const std::string &srcPath, const std::string &dstDir); + + bool beginsWith(const std::string &container, const std::string &str); }} -#include "helpers.hpp" +#include "internal.hpp" -#endif // __GALLERY_MODEL_HELPERS_H__ +#endif // __GALLERY_MODEL_INTERNAL_H__ diff --git a/src/model/helpers.hpp b/src/model/internal.hpp index be253bf..90b8751 100644 --- a/src/model/helpers.hpp +++ b/src/model/internal.hpp @@ -29,3 +29,11 @@ namespace gallery { return MediaType::OTHERS; } } + +namespace gallery { namespace util { + + inline bool beginsWith(const std::string &container, const std::string &str) + { + return (container.compare(0, str.size(), str) == 0); + } +}} diff --git a/src/presenters/AlertDialog.cpp b/src/presenters/AlertDialog.cpp index fac680f..0a23f48 100644 --- a/src/presenters/AlertDialog.cpp +++ b/src/presenters/AlertDialog.cpp @@ -241,10 +241,9 @@ namespace gallery { eext_object_event_callback_del(*m_popup, EEXT_CALLBACK_BACK, CALLBACK_A(AlertDialog::onPopupHWBackKey)); - m_popup.reset(); - deactivateBy(m_popup.get()); broadcastActivateBy(this); + m_popup.reset(); m_rc->unref(); } } diff --git a/src/presenters/Instance.cpp b/src/presenters/Instance.cpp index 3e8a104..f4c7ce9 100644 --- a/src/presenters/Instance.cpp +++ b/src/presenters/Instance.cpp @@ -28,6 +28,7 @@ #include "presenters/NoContentPage.h" #include "presenters/ThumbnailPage.h" #include "presenters/ViewerPage.h" +#include "presenters/VideoPlayerPage.h" #include "resources.h" #include "common.h" @@ -63,11 +64,6 @@ namespace gallery { { m_context = context; - m_gallery = Gallery::newInstance(); - if (!m_gallery) { - LOG_RETURN(RES_FAIL, "Gallery::newInstance() failed!"); - } - m_win = m_context->getWindow(); FAIL_RETURN(setupTheme(), "setupTheme() failed!"); @@ -86,9 +82,6 @@ namespace gallery { m_sysEventProvider.addEventHandler( DELEGATE(Instance::onSysEvent, this)); - m_gallery->getAlbum()->addChangeHandler(WEAK_DELEGATE( - Instance::onAlbumChanged, asWeak(*this))); - return RES_OK; } @@ -109,12 +102,16 @@ namespace gallery { void Instance::onPause() { DLOG("PAUSE"); + + setInstancePaused(*m_win, true); } void Instance::onResume() { DLOG("RESUME"); + setInstancePaused(*m_win, false); + if (SCAN_MEDIA_ON_RESUME) { rescanMediaContent(); } @@ -206,6 +203,8 @@ namespace gallery { Result Instance::handleSingleMode(const std::string &operation, app_control_h appControl) { + FAIL_RETURN(ensureGalleryModel(), "ensureGalleryModel() failed!"); + if (!m_page) { if (isEmpty(m_gallery->getAlbum())) { createNoContentPage(); @@ -231,7 +230,24 @@ namespace gallery { std::string uri; FAIL_RETURN(util::getNz(app_control_get_uri, uri, appControl), "app_control_get_uri() failed!"); - createViewerPage(uri); + + const auto media = MediaItem::newInstance(std::move(uri)); + if (!media) { + LOG_RETURN(RES_FAIL, "MediaItem::newInstance() failed!"); + } + + switch (media->getType()) { + case MediaType::IMAGE: + createViewerPage(media); + break; + case MediaType::VIDEO: + createVideoPlayerPage(media); + break; + default: + LOG_RETURN(RES_NOT_SUPPORTED, + "Media type is not supported: %d;", media->getType()); + } + } else { WLOG("Operation not supported for current mode!"); return RES_NOT_SUPPORTED; @@ -242,6 +258,23 @@ namespace gallery { return RES_OK; } + Result Instance::ensureGalleryModel() + { + if (m_gallery) { + return RES_FALSE; + } + + m_gallery = Gallery::newInstance(); + if (!m_gallery) { + LOG_RETURN(RES_FAIL, "Gallery::newInstance() failed!"); + } + + m_gallery->getAlbum()->addChangeHandler(WEAK_DELEGATE( + Instance::onAlbumChanged, asWeak(*this))); + + return RES_OK; + } + void Instance::createNoContentPage() { DLOG("Creating NoContentPage."); @@ -252,20 +285,30 @@ namespace gallery { void Instance::createThumbnailPage() { DLOG("Creating ThumbnailPage."); + FAIL_RETURN_VOID(ensureGalleryModel(), "ensureGalleryModel() failed!"); + m_page = ThumbnailPage::Builder().setNaviframe(m_navi). setAlbum(m_gallery->getAlbum()). build(DELEGATE(Instance::onPageExitRequest, this)); } - void Instance::createViewerPage(std::string filePath) + void Instance::createViewerPage(const MediaItemSRef &media) { DLOG("Creating ViewerPage."); m_page = ViewerPage::Builder().setNaviframe(m_navi). - setMedia(MediaItem::newInstance(std::move(filePath))). + setMedia(media). setExitOnZoomOut(false). build(DELEGATE(Instance::onPageExitRequest, this)); } + void Instance::createVideoPlayerPage(const MediaItemSRef &media) + { + DLOG("Creating VideoPlayerPage."); + m_page = VideoPlayerPage::Builder().setNaviframe(m_navi). + setMedia(media). + build(DELEGATE(Instance::onPageExitRequest, this)); + } + void Instance::onAlbumChanged() { if (isEmpty(m_gallery->getAlbum())) { diff --git a/src/presenters/MoreOptionsPresenter.cpp b/src/presenters/MoreOptionsPresenter.cpp index fa2d059..ad0f74d 100644 --- a/src/presenters/MoreOptionsPresenter.cpp +++ b/src/presenters/MoreOptionsPresenter.cpp @@ -143,6 +143,8 @@ namespace gallery { m_widget->addEventHandler(impl::MORE_ITEM_SELECTED, WEAK_DELEGATE( MoreOptionsPresenter::onItemSelected, asWeak(*this))); + deactivateBy(m_widget.get()); + return RES_OK; } diff --git a/src/presenters/Presenter.cpp b/src/presenters/Presenter.cpp index e461cda..dd29ddb 100644 --- a/src/presenters/Presenter.cpp +++ b/src/presenters/Presenter.cpp @@ -42,18 +42,23 @@ namespace gallery { Result Presenter::prepare(ElmWidget &widget) { - m_topWidget = asWeak(asWidget(widget.getTopWidget())); - if (!m_topWidget) { - LOG_RETURN(RES_FAIL, "m_topWidget is NULL!"); + m_window = asShared(widget.getWindow()); + if (!m_window) { + LOG_RETURN(RES_FAIL, "m_window is NULL!"); } - addDeactivatorSource(*m_topWidget); + addDeactivatorSource(*m_window); m_isPrepared = true; return RES_OK; } + Window &Presenter::getWindow() + { + return *m_window; + } + void Presenter::addDeactivatorSource(Widget &source) { source.addEventHandler(impl::ACTIVATE_BY, @@ -99,11 +104,7 @@ namespace gallery { void Presenter::broadcastDeactivator(const SmartEvent event, void *const deactivator) { - if (m_topWidget) { - sendDeactivatorInfo(*m_topWidget, event, {deactivator, true}); - } else { - ELOG("m_topWidget is NULL!"); - } + sendDeactivatorInfo(*m_window, event, {deactivator, true}); } void Presenter::sendDeactivatorInfo(Widget &sender, diff --git a/src/presenters/VideoPlayerPage.cpp b/src/presenters/VideoPlayerPage.cpp new file mode 100644 index 0000000..b286ec9 --- /dev/null +++ b/src/presenters/VideoPlayerPage.cpp @@ -0,0 +1,589 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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 "presenters/VideoPlayerPage.h" + +#include <efl_util.h> + +#include "model/MediaItem.h" + +#include "view/TouchParser.h" + +#include "resources.h" +#include "common.h" + +namespace gallery { namespace { namespace impl { + + using namespace ucl; + + constexpr auto AUTO_START_TIMEOUT_SEC = 0.3; + constexpr auto CONTROLS_PLAYBACK_HIDE_TIMEOUT_SEC = 3.0; + constexpr auto CONTROLS_PAUSE_HIDE_TIMEOUT_SEC = 6.0; + + constexpr auto TICK_TIMER_INTERVAL_SEC = 0.1; + + constexpr auto TIME_SEC_MS = 1000; + constexpr auto TIME_MIN_SEC = 60; + constexpr auto TIME_HOUR_MIN = 60; + + const TString TIME_SHORT_FORMAT {"%02d:%02d / %02d:%02d"}; + const TString TIME_LONG_FORMAT {"%02d:%02d:%02d / %02d:%02d:%02d"}; + + constexpr LayoutTheme LAYOUT_VIDEO_PLAYER + {"layout", "gallery", "video_player"}; + + constexpr ElmStyle STYLE_VOLUME_ON_BTN {"gallery_video_volume_on"}; + constexpr ElmStyle STYLE_VOLUME_MUTE_BTN {"gallery_video_volume_mute"}; + constexpr ElmStyle STYLE_PLAY_BTN {"gallery_video_play"}; + constexpr ElmStyle STYLE_PAUSE_BTN {"gallery_video_pause"}; + + constexpr EdjePart PART_VOLUME_ON_BTN {"gallery.swallow.volume_on"}; + constexpr EdjePart PART_VOLUME_MUTE_BTN {"gallery.swallow.volume_mute"}; + constexpr EdjePart PART_PLAY_BTN {"gallery.swallow.play"}; + constexpr EdjePart PART_PAUSE_BTN {"gallery.swallow.pause"}; + + constexpr EdjeSignal SHOW_VOLUME_ON_BTN {"show,volume_on,btn"}; + constexpr EdjeSignal SHOW_VOLUME_MUTE_BTN {"show,volume_mute,btn"}; + constexpr EdjeSignal HIDE_VOLUME_BTN {"hide,volume,btn"}; + + constexpr EdjeSignal SHOW_PLAY_BTN {"show,play,btn"}; + constexpr EdjeSignal SHOW_PAUSE_BTN {"show,pause,btn"}; + constexpr EdjeSignal HIDE_PLAY_PAUSE_BTN {"hide,play_pause,btn"}; + + constexpr EdjeSignal SHOW_TEXT {"show,text"}; + constexpr EdjeSignal HIDE_TEXT {"hide,text"}; + + void timeMsToHMS(int timeMs, int &h, int &m, int &s) + { + int t = (timeMs / TIME_SEC_MS); + s = (t % TIME_MIN_SEC); + t /= TIME_MIN_SEC; + m = (t % TIME_HOUR_MIN); + h = (t / TIME_HOUR_MIN); + } +}}} + +namespace gallery { + + using namespace ucl; + + // VideoPlayerPage::Builder // + + VideoPlayerPage::Builder::Builder() + { + } + + VideoPlayerPage::Builder::~Builder() + { + } + + VideoPlayerPage::Builder &VideoPlayerPage::Builder::setNaviframe( + const NaviframeSRef &navi) + { + m_navi = navi; + return *this; + } + + VideoPlayerPage::Builder &VideoPlayerPage::Builder::setMedia( + const MediaItemSRef &media) + { + m_media = media; + return *this; + } + + VideoPlayerPageWRef VideoPlayerPage::Builder::build( + const ExitRequestHandler onExitRequest) const + { + if (!onExitRequest) { + LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, + "onExitRequest is NULL"); + } + if (!m_navi) { + LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "m_navi is NULL"); + } + if (!m_media) { + LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "m_media is NULL"); + } + if (m_media->getType() != MediaType::VIDEO) { + LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "Wrong media type"); + } + + auto result = makeShared<VideoPlayerPage>( + m_navi, onExitRequest, m_media); + + FAIL_RETURN_VALUE(result->prepare(), {}, "result->prepare() failed!"); + + return result; + } + + // VideoPlayerPage // + + VideoPlayerPage::VideoPlayerPage(RefCountObjBase &rc, + const NaviframeSRef &navi, + const ExitRequestHandler onExitRequest, + const MediaItemSRef &media) : + Page(rc, navi, onExitRequest), + m_media(media), + m_player(), + m_videoDuration(0), + m_autoStartTimer(nullptr), + m_controlsHideTimer(nullptr), + m_tickTimer(nullptr), + m_state(State::PAUSED), + m_isControlsVisible(false), + m_needAutoStart(true) + { + } + + VideoPlayerPage::~VideoPlayerPage() + { + setScreenOlwaysOn(false); + + delRotaryEventHandler(CALLBACK_A(VideoPlayerPage::onRotary), this); + + stopTimer(m_autoStartTimer); + stopTimer(m_controlsHideTimer); + stopTimer(m_tickTimer); + + if (m_player) { + if (getPlayerState() != PLAYER_STATE_IDLE) { + FAIL_LOG(util::call(player_unprepare, m_player), + "player_unprepare() failed!"); + } + FAIL_LOG(util::call(player_destroy, m_player), + "player_destroy() failed!"); + } + } + + Result VideoPlayerPage::prepare() + { + m_content = Layout::Builder(). + setTheme(impl::LAYOUT_VIDEO_PLAYER). + setIsOwner(true). + build(getNaviframe()); + if (!m_content) { + LOG_RETURN(RES_FAIL, "m_content is NULL"); + } + + FAIL_RETURN(Page::prepare( + [this]() + { + return getNaviframe().push(*m_content, NAVIFRAME_EMPTY); + }), + "Page::prepare() failed!"); + + createImage(); + FAIL_RETURN(preparePlayer(), "preparePlayer() failed!"); + + createControls(); + updatePlayTimeText(); + + m_touchParser = makeShared<TouchParser>(*m_image); + m_touchParser->setTapHandler( + DELEGATE(VideoPlayerPage::onTap, this)); + + getWindow().addEventHandler(INSTANCE_PAUSED, WEAK_DELEGATE( + VideoPlayerPage::onInstancePaused, asWeak(*this))); + getWindow().addEventHandler(INSTANCE_RESUMED, WEAK_DELEGATE( + VideoPlayerPage::onInstanceResumed, asWeak(*this))); + + addRotaryEventHandler(CALLBACK_A(VideoPlayerPage::onRotary), this); + + if (!isInstancePaused(getWindow())) { + resetAutoStartTimer(); + } + + return RES_OK; + } + + void VideoPlayerPage::createImage() + { + m_image = makeShared<Widget>( + evas_object_image_filled_add(m_content->getEvas())); + show(*m_image); + + m_content->setContent(*m_image); + } + + Result VideoPlayerPage::preparePlayer() + { + FAIL_RETURN(util::getNz(player_create, m_player), + "player_create() failed!"); + + FAIL_RETURN(util::call(player_set_display_mode, m_player, + PLAYER_DISPLAY_MODE_CROPPED_FULL), + "player_set_display_mode() failed!"); + + FAIL_RETURN(util::call(player_set_display, m_player, + PLAYER_DISPLAY_TYPE_EVAS, GET_DISPLAY(m_image->getEo())), + "player_set_display() failed!"); + + FAIL_RETURN(util::call(player_set_uri, m_player, + m_media->getFilePath().c_str()), + "player_set_uri() failed!"); + + FAIL_RETURN(util::call(player_prepare, m_player), + "player_prepare() failed!"); + + FAIL_RETURN(util::getNz(player_get_duration, m_videoDuration, m_player), + "player_get_duration() failed!"); + + FAIL_RETURN(seekToStart(), "seekToStart() failed!"); + + FAIL_RETURN(util::call(player_set_completed_cb, m_player, + CALLBACK_B(VideoPlayerPage::onPlaybackComplete), this), + "player_set_completed_cb() failed!"); + + FAIL_RETURN(util::call(player_set_interrupted_cb, m_player, + CALLBACK_B(VideoPlayerPage::onPlaybackInterrupted), + this), "player_set_interrupted_cb() failed!"); + + return RES_OK; + } + + Result VideoPlayerPage::seekToStart() + { + FAIL_RETURN(util::call(player_set_play_position, m_player, true, 0, + CALLBACK_B(VideoPlayerPage::onSeekComplete), this), + "player_set_play_position() failed!"); + return RES_OK; + } + + void VideoPlayerPage::createControls() + { + createButton(impl::STYLE_VOLUME_ON_BTN, impl::PART_VOLUME_ON_BTN, + WEAK_DELEGATE(VideoPlayerPage::onVolumeOnBtnClick, + asWeak(*this))); + + createButton(impl::STYLE_VOLUME_MUTE_BTN, impl::PART_VOLUME_MUTE_BTN, + WEAK_DELEGATE(VideoPlayerPage::onVolumeMuteBtnClick, + asWeak(*this))); + + createButton(impl::STYLE_PLAY_BTN, impl::PART_PLAY_BTN, + WEAK_DELEGATE(VideoPlayerPage::onPlayBtnClick, + asWeak(*this))); + + createButton(impl::STYLE_PAUSE_BTN, impl::PART_PAUSE_BTN, + WEAK_DELEGATE(VideoPlayerPage::onPauseBtnClick, + asWeak(*this))); + } + + void VideoPlayerPage::createButton(const ElmStyle style, + const EdjePart part, const WidgetEventHandler &handler) + { + const auto btn = makeShared<StyledWidget>(elm_button_add(*m_content)); + btn->setStyle(style); + show(*btn); + + m_content->setContent(*btn, part); + + btn->addEventHandler(BTN_CLICKED, handler); + } + + bool VideoPlayerPage::resetTimer(Ecore_Timer *&timer, + const double timeout, Ecore_Task_Cb func) + { + stopTimer(timer); + + timer = ecore_timer_add(timeout, func, this); + if (!timer) { + LOG_RETURN_VALUE(RES_FAIL, false, "ecore_timer_add() failed!"); + } + + return true; + } + + void VideoPlayerPage::stopTimer(Ecore_Timer *&timer) + { + if (timer) { + ecore_timer_del(timer); + timer = nullptr; + } + } + + bool VideoPlayerPage::resetAutoStartTimer() + { + return resetTimer(m_autoStartTimer, impl::AUTO_START_TIMEOUT_SEC, + CALLBACK_A(VideoPlayerPage::onAutoStartTimer)); + } + + bool VideoPlayerPage::resetControlsHideTimer() + { + if (m_isPlaybackCompleted) { + stopTimer(m_controlsHideTimer); + return true; + } + return resetTimer(m_controlsHideTimer, + ((m_state == State::PLAYING) ? + impl::CONTROLS_PLAYBACK_HIDE_TIMEOUT_SEC : + impl::CONTROLS_PAUSE_HIDE_TIMEOUT_SEC + ), + CALLBACK_A(VideoPlayerPage::onControlsHideTimer)); + } + + bool VideoPlayerPage::resetTickTimer() + { + return resetTimer(m_tickTimer, impl::TICK_TIMER_INTERVAL_SEC, + CALLBACK_A(VideoPlayerPage::onTickTimer)); + } + + void VideoPlayerPage::showControls() + { + if (!m_isControlsVisible) { + m_isControlsVisible = true; + + if (m_state == State::PLAYING) { + m_content->emit(impl::SHOW_PAUSE_BTN); + resetTickTimer(); + updatePlayTimeText(); + } else { + m_content->emit(impl::SHOW_PLAY_BTN); + } + + m_content->emit(impl::SHOW_TEXT); + } + + resetControlsHideTimer(); + } + + void VideoPlayerPage::hideControls() + { + if (m_isControlsVisible) { + m_isControlsVisible = false; + + m_content->emit(impl::HIDE_VOLUME_BTN); + m_content->emit(impl::HIDE_PLAY_PAUSE_BTN); + m_content->emit(impl::HIDE_TEXT); + + stopTimer(m_tickTimer); + stopTimer(m_controlsHideTimer); + } + } + + bool VideoPlayerPage::updatePlayTimeText() + { + int playPosition = 0; + + if (isBad(util::get(player_get_play_position, + playPosition, m_player))) { + ELOG("player_get_play_position() failed!"); + return false; + } + + updatePlayTimeText(std::min(playPosition, m_videoDuration)); + + return true; + } + + void VideoPlayerPage::updatePlayTimeText(const int timeMs) + { + int posH = 0; + int posM = 0; + int posS = 0; + impl::timeMsToHMS(timeMs, posH, posM, posS); + + int durH = 0; + int durM = 0; + int durS = 0; + impl::timeMsToHMS(m_videoDuration, durH, durM, durS); + + if (durH == 0) { + m_content->setText(impl::TIME_SHORT_FORMAT.format( + posM, posS, durM, durS)); + } else { + m_content->setText(impl::TIME_LONG_FORMAT.format( + posH, posM, posS, durH, durM, durS)); + } + } + + player_state_e VideoPlayerPage::getPlayerState() const + { + player_state_e result = PLAYER_STATE_NONE; + FAIL_LOG(util::getNz(player_get_state, result, m_player), + "player_get_state() failed!"); + return result; + } + + void VideoPlayerPage::startPlayback() + { + if (m_state != State::PAUSED) { + return; + } + m_state = State::PLAYING; + + const auto playerState = getPlayerState(); + switch (playerState) { + case PLAYER_STATE_READY: + case PLAYER_STATE_PAUSED: + if (m_isPlaybackCompleted) { + m_isPlaybackCompleted = false; + FAIL_LOG(seekToStart(), "seekToStart() failed!"); + } else { + FAIL_LOG(util::call(player_start, m_player), + "player_start() failed!"); + } + break; + default: + WLOG("Unexpected player state: %d;", playerState); + break; + } + + setScreenOlwaysOn(true); + resetTickTimer(); + stopTimer(m_autoStartTimer); + m_needAutoStart = false; + + if (m_isControlsVisible) { + m_content->emit(impl::SHOW_PAUSE_BTN); + } + } + + void VideoPlayerPage::pausePlayback() + { + if (m_state != State::PLAYING) { + return; + } + m_state = State::PAUSED; + + const auto playerState = getPlayerState(); + switch (playerState) { + case PLAYER_STATE_PLAYING: + FAIL_LOG(util::call(player_pause, m_player), + "player_pause() failed!"); + break; + default: + WLOG("Unexpected player state: %d;", playerState); + break; + } + + setScreenOlwaysOn(false); + stopTimer(m_tickTimer); + updatePlayTimeText(); + + if (m_isControlsVisible) { + m_content->emit(impl::SHOW_PLAY_BTN); + } + } + + void VideoPlayerPage::setScreenOlwaysOn(bool isAlwaysOn) + { + efl_util_set_window_screen_mode(getWindow(), + (isAlwaysOn ? EFL_UTIL_SCREEN_MODE_ALWAYS_ON : + EFL_UTIL_SCREEN_MODE_DEFAULT)); + } + + void VideoPlayerPage::onPlaybackComplete() + { + m_isPlaybackCompleted = true; + pausePlayback(); + showControls(); + } + + void VideoPlayerPage::onPlaybackInterrupted(player_interrupted_code_e code) + { + if (code != PLAYER_INTERRUPTED_COMPLETED) { + pausePlayback(); + showControls(); + } + } + + void VideoPlayerPage::onSeekComplete() + { + if ((m_state == State::PLAYING) && + (getPlayerState() != PLAYER_STATE_PLAYING)) { + FAIL_LOG(util::call(player_start, m_player), + "player_start() failed!"); + } + } + + Eina_Bool VideoPlayerPage::onAutoStartTimer() + { + m_autoStartTimer = nullptr; + + startPlayback(); + + return ECORE_CALLBACK_CANCEL; + } + + Eina_Bool VideoPlayerPage::onControlsHideTimer() + { + m_controlsHideTimer = nullptr; + + hideControls(); + + return ECORE_CALLBACK_CANCEL; + } + + Eina_Bool VideoPlayerPage::onTickTimer() + { + if (!updatePlayTimeText()) { + m_tickTimer = nullptr; + return ECORE_CALLBACK_CANCEL; + } + return ECORE_CALLBACK_RENEW; + } + + void VideoPlayerPage::onVolumeOnBtnClick(Widget &sender, void *eventInfo) + { + } + + void VideoPlayerPage::onVolumeMuteBtnClick(Widget &sender, void *eventInfo) + { + } + + void VideoPlayerPage::onPlayBtnClick(Widget &sender, void *eventInfo) + { + startPlayback(); + resetControlsHideTimer(); + } + + void VideoPlayerPage::onPauseBtnClick(Widget &sender, void *eventInfo) + { + pausePlayback(); + resetControlsHideTimer(); + } + + void VideoPlayerPage::onTap(int x, int y) + { + if (m_isControlsVisible) { + hideControls(); + } else { + showControls(); + } + } + + Eina_Bool VideoPlayerPage::onRotary(Eext_Rotary_Event_Info *info) + { + showControls(); + return EINA_TRUE; + } + + void VideoPlayerPage::onInstancePaused(Widget &sender, void *eventInfo) + { + pausePlayback(); + stopTimer(m_autoStartTimer); + stopTimer(m_controlsHideTimer); + } + + void VideoPlayerPage::onInstanceResumed(Widget &sender, void *eventInfo) + { + if (m_needAutoStart) { + resetAutoStartTimer(); + } else { + showControls(); + } + } +} diff --git a/src/presenters/ViewerPage.cpp b/src/presenters/ViewerPage.cpp index c736755..ffa3784 100644 --- a/src/presenters/ViewerPage.cpp +++ b/src/presenters/ViewerPage.cpp @@ -94,6 +94,9 @@ namespace gallery { if (!m_media) { LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "m_media is NULL"); } + if (m_media->getType() != MediaType::IMAGE) { + LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "Wrong media type"); + } auto result = makeShared<ViewerPage>( m_navi, onExitRequest, m_media, m_exitOnZoomOut); diff --git a/src/presenters/common.h b/src/presenters/common.h index a171f48..8a5ab5c 100644 --- a/src/presenters/common.h +++ b/src/presenters/common.h @@ -17,6 +17,8 @@ #ifndef __GALLERY_PRESENTERS_COMMON_H__ #define __GALLERY_PRESENTERS_COMMON_H__ +#include "internal.h" + #include "../view/common.h" #endif // __GALLERY_PRESENTERS_COMMON_H__ diff --git a/src/presenters/internal.cpp b/src/presenters/internal.cpp new file mode 100644 index 0000000..33cf63f --- /dev/null +++ b/src/presenters/internal.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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 "internal.h" + +#include "common.h" + +namespace gallery { namespace { namespace impl { + + using namespace ucl; + + constexpr EoDataKey INSTANCE_PTR {"gallery,instance,data,ptr"}; +}}} + +namespace gallery { + + using namespace ucl; + + void setInstancePaused(ucl::Window &win, const bool value) + { + win.setData(impl::INSTANCE_PTR, reinterpret_cast<void *>(value)); + win.callEvent((value ? INSTANCE_PAUSED : INSTANCE_RESUMED), nullptr); + } + + bool isInstancePaused(const ucl::Window &win) + { + return (reinterpret_cast<intptr_t>( + win.getData(impl::INSTANCE_PTR)) != 0); + } +} diff --git a/src/presenters/internal.h b/src/presenters/internal.h new file mode 100644 index 0000000..a0f7ecb --- /dev/null +++ b/src/presenters/internal.h @@ -0,0 +1,31 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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 __GALLERY_PRESENTERS_INTERNAL_H__ +#define __GALLERY_PRESENTERS_INTERNAL_H__ + +#include "ucl/gui/Window.h" + +namespace gallery { + + constexpr ucl::SmartEvent INSTANCE_PAUSED {"gallery,instance,paused"}; + constexpr ucl::SmartEvent INSTANCE_RESUMED {"gallery,instance,resumed"}; + + void setInstancePaused(ucl::Window &win, bool value); + bool isInstancePaused(const ucl::Window &win); +} + +#endif // __GALLERY_PRESENTERS_INTERNAL_H__ diff --git a/tizen-manifest.xml b/tizen-manifest.xml index daf60fd..f0f7f5e 100644 --- a/tizen-manifest.xml +++ b/tizen-manifest.xml @@ -7,6 +7,7 @@ <app-control> <operation name="http://tizen.org/appcontrol/operation/view"/> <mime name="image/*"/> + <mime name="video/*"/> </app-control> </ui-application> <privileges> |