summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Nazarov <i.nazarov@samsung.com>2017-05-16 21:26:00 +0300
committerIgor Nazarov <i.nazarov@samsung.com>2017-05-18 13:38:19 +0300
commite0ddb963aeb9ec8eeaf2b27c3c61f6c73c9789b3 (patch)
tree39745a07881aef25ce016b9f27b45c7333d21e94
parentaa6a3b29c3d5fc23dcb19a52e8943e5a7bc596b0 (diff)
downloadgallery-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.h5
-rw-r--r--inc/presenters/Presenter.h6
-rw-r--r--inc/presenters/VideoPlayerPage.h120
-rw-r--r--inc/presenters/types.h1
-rw-r--r--src/common.h2
-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.cpp28
-rw-r--r--src/model/common.h2
-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.cpp3
-rw-r--r--src/presenters/Instance.cpp65
-rw-r--r--src/presenters/MoreOptionsPresenter.cpp2
-rw-r--r--src/presenters/Presenter.cpp19
-rw-r--r--src/presenters/VideoPlayerPage.cpp589
-rw-r--r--src/presenters/ViewerPage.cpp3
-rw-r--r--src/presenters/common.h2
-rw-r--r--src/presenters/internal.cpp43
-rw-r--r--src/presenters/internal.h31
-rw-r--r--tizen-manifest.xml1
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>