TizenRefApp-8425 [Gallery] Implement remove MediaItem objects in model 07/127007/2
authorIgor Nazarov <i.nazarov@samsung.com>
Tue, 25 Apr 2017 15:56:14 +0000 (18:56 +0300)
committerIgor Nazarov <i.nazarov@samsung.com>
Tue, 25 Apr 2017 16:37:07 +0000 (19:37 +0300)
- Implemented BaseJob class for async job execution in thread;
- Implemented MediaItem::RemoverJob for remuving multiple media items;
- added force progress feature into ProcessingPresenter;
- ImageGrid bug fix when switching select mode;
- added recursuve feature into ucl::Mutex.

Change-Id: I0b1017096e5f4d36e0728150959f54398042a838

15 files changed:
inc/model/IJob.h [new file with mode: 0644]
inc/model/MediaItem.h
inc/model/types.h
inc/presenters/ProcessingPresenter.h
inc/types.h
src/model/BaseJob.cpp [new file with mode: 0644]
src/model/BaseJob.h [new file with mode: 0644]
src/model/GalleryAlbum.cpp
src/model/MediaItem.cpp
src/model/helpers.cpp
src/model/helpers.h
src/presenters/ProcessingPresenter.cpp
src/view/ImageGrid.cpp
ucl/inc/ucl/util/threading/Mutex.h
ucl/inc/ucl/util/threading/Mutex.hpp

diff --git a/inc/model/IJob.h b/inc/model/IJob.h
new file mode 100644 (file)
index 0000000..66fc5ce
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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_MODEL_I_JOB_H__
+#define __GALLERY_MODEL_I_JOB_H__
+
+#include "types.h"
+
+namespace gallery {
+
+       class IJob : public ucl::Polymorphic {
+       public:
+               virtual ucl::Result getResult() const = 0;
+               virtual bool isCancelable() const = 0;
+               virtual ucl::Result cancel() = 0;
+       };
+}
+
+#endif // __GALLERY_MODEL_I_JOB_H__
index fb0310d7978a2d6a80e35a3a00ba759d8ccc2bc9..c1210e5f13792c212c8b094a9556f8074937afd5 100644 (file)
@@ -26,6 +26,15 @@ namespace gallery {
                using ThumbnailPathGetCb =
                                ucl::Delegate<void(ucl::Result, const std::string &path)>;
 
+               class Remover;
+               class RemoverBuilder final {
+               public:
+                       RemoverBuilder &setItems(MediaItems items);
+                       IJobSRef build(const NotiHandler &onComplete) const;
+               private:
+                       ucl::SharedRef<MediaItems> m_items;
+               };
+
        public:
                static MediaItemSRef newInstance(media_info_h media);
                virtual ~MediaItem();
index b907517de7b6ae05e035be48684d97cd6e316bff..0ee204045690fa1de8692d4265ce9302033d7663 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef __GALLERY_MODEL_TYPES_H__
 #define __GALLERY_MODEL_TYPES_H__
 
+#include <vector>
+
 #include <media_content.h>
 
 #include "../types.h"
@@ -31,11 +33,15 @@ namespace gallery {
                OTHERS
        };
 
+       UCL_DECLARE_REF_ALIASES(IJob);
+
        UCL_DECLARE_REF_ALIASES(Gallery);
 
        UCL_DECLARE_REF_ALIASES(IMediaAlbum);
 
        UCL_DECLARE_REF_ALIASES(MediaItem);
+
+       using MediaItems = std::vector<MediaItemSRef>;
 }
 
 #endif // __GALLERY_MODEL_TYPES_H__
index 27e9f816b0eec2e9188d3b5cb2d83ae858588224..58e5a3a8338f027174891705054a31b97065772a 100644 (file)
@@ -37,11 +37,13 @@ namespace gallery {
                        Builder &setProcessingText(ucl::TString text);
                        Builder &setCompleteText(ucl::TString text);
                        Builder &setIconType(IconType value);
+                       Builder &setForceProgress(bool value);
                        ProcessingPresenterSRef build(ucl::Widget &parent) const;
                private:
                        ucl::TString m_processingText;
                        ucl::TString m_completeText;
                        IconType m_iconType;
+                       bool m_forceProgress;
                };
 
                using DismissHandler = ucl::WeakDelegate<void()>;
@@ -58,7 +60,8 @@ namespace gallery {
 
                ucl::Result prepare(ucl::Widget &parent,
                                const ucl::TString &processingText,
-                               const ucl::TString &completeText);
+                               const ucl::TString &completeText,
+                               bool forceProgress);
 
                ucl::Result createWidget(ucl::Widget &parent,
                                const ucl::TString &processingText);
@@ -69,6 +72,8 @@ namespace gallery {
                bool resetTimer(double timeout);
                void stopTimer();
 
+               void showProgress();
+
                void tryShowPopup();
                void dismissPopup(bool force = false);
 
index c5b7426c2a15273cd4d3a89aac5f69f1a0e39ec4..898385508f91cb354ccf3bd945f7688ff478997e 100644 (file)
 #include "ucl/util/memory.h"
 #include "ucl/util/delegation.h"
 
+#include "ucl/misc/smartDelegation.h"
+
+namespace gallery {
+
+       using NotiHandler = ucl::WeakDelegate<void()>;
+}
+
 #endif // __GALLERY_TYPES_H__
diff --git a/src/model/BaseJob.cpp b/src/model/BaseJob.cpp
new file mode 100644 (file)
index 0000000..fb61c32
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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 "BaseJob.h"
+
+#include <Ecore.h>
+
+#include "common.h"
+
+namespace gallery {
+
+       using namespace ucl;
+
+       BaseJob::BaseJob(const NotiHandler &onComplete,
+                       const bool isCancelable) :
+               m_onComplete(onComplete),
+               m_isCancelable(isCancelable),
+               m_result(RES_ILLEGAL_STATE),
+               m_selfPtr(new BaseJob *(this)),
+               m_isCancelled(0)
+       {
+       }
+
+       BaseJob::~BaseJob()
+       {
+               finish();
+       }
+
+       Result BaseJob::prepare()
+       {
+               if (!m_thread.start(
+                       [this]()
+                       {
+                               m_result = execute();
+                               notifyCompleteAsync();
+                       }
+                       )) {
+                       LOG_RETURN(RES_FAIL, "m_thread->start() failed!");
+               }
+               return RES_OK;
+       }
+
+       Result BaseJob::getResult() const
+       {
+               if (!m_thread.wasJoinded()) {
+                       LOG_RETURN(RES_ILLEGAL_STATE, "Job is not complete!");
+               }
+               return m_result;
+       }
+
+       bool BaseJob::isCancelable() const
+       {
+               return m_isCancelable;
+       }
+
+       Result BaseJob::cancel()
+       {
+               if (!m_isCancelable) {
+                       LOG_RETURN(RES_NOT_SUPPORTED, "Not cancelable!");
+               }
+               if (isCancelled()) {
+                       return RES_FALSE;
+               }
+               m_isCancelled.store(true);
+               return RES_OK;
+       }
+
+       bool BaseJob::isCancelled() const
+       {
+               return m_isCancelled.load();
+       }
+
+       void BaseJob::notifyCompleteAsync()
+       {
+               ecore_main_loop_thread_safe_call_async(
+                       [](void *data)
+                       {
+                               const auto selfPtr = static_cast<BaseJob **>(data);
+                               const auto self = *selfPtr;
+                               delete selfPtr;
+                               if (self) {
+                                       self->m_selfPtr = nullptr;
+                                       self->finish();
+                               }
+                       },
+                       m_selfPtr);
+       }
+
+       void BaseJob::finish()
+       {
+               if (!m_thread.wasJoinded()) {
+                       m_thread.join();
+                       if (m_selfPtr) {
+                               *m_selfPtr = nullptr;
+                       }
+                       if (m_onComplete) {
+                               m_onComplete();
+                       }
+               }
+       }
+}
diff --git a/src/model/BaseJob.h b/src/model/BaseJob.h
new file mode 100644 (file)
index 0000000..939d88e
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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_MODEL_BASE_JOB_H__
+#define __GALLERY_MODEL_BASE_JOB_H__
+
+#include <atomic>
+
+#include "ucl/util/threading/Thread.h"
+
+#include "model/IJob.h"
+#include "types.h"
+
+namespace gallery {
+
+       class BaseJob : public IJob {
+       public:
+               // IJob //
+
+               virtual ucl::Result getResult() const final override;
+               virtual bool isCancelable() const final override;
+               virtual ucl::Result cancel() final override;
+
+       protected:
+               BaseJob(const NotiHandler &onComplete,
+                               bool isCancelable);
+               virtual ~BaseJob();
+
+               ucl::Result prepare();
+
+               bool isCancelled() const;
+
+               virtual ucl::Result execute() = 0;
+
+       private:
+               void notifyCompleteAsync();
+               void finish();
+
+       private:
+               const NotiHandler m_onComplete;
+               const bool m_isCancelable;
+               ucl::Thread m_thread;
+               ucl::Result m_result;
+               BaseJob **m_selfPtr;
+               std::atomic_int m_isCancelled;
+       };
+}
+
+#endif // __GALLERY_MODEL_BASE_JOB_H__
index 33f08e8ad875e854035fab772df83c1d595c113b..68326d9a90e29bc3043c31ae5a7d98676481088b 100644 (file)
@@ -86,6 +86,8 @@ namespace gallery {
 
        Result GalleryAlbum::forEachMedia(EachCb cb) const
        {
+               MutexLock lock(getMediaMutex());
+
                const int ret = media_info_foreach_media_from_db(m_filter,
                        [](media_info_h media, void *user_data)
                        {
@@ -108,12 +110,15 @@ namespace gallery {
 
        Result GalleryAlbum::getMediaCount(int &count) const
        {
+               MutexLock lock(getMediaMutex());
+
                const int ret = media_info_get_media_count_from_db(m_filter, &count);
                if ((ret != 0) || (count < 0)) {
                        count = 0;
                        LOG_RETURN(RES_FAIL,
                                        "media_info_foreach_media_from_db() failed: %d", ret);
                }
+
                return RES_OK;
        }
 }
index 39c4b6fb4e19eaefab9c25ab4afd188aa1f47b6e..dead384857dcd1c2e044bc88b8e51005656120b2 100644 (file)
 
 #include <Ecore_File.h>
 
+#include "BaseJob.h"
+
 #include "common.h"
 
 namespace gallery {
 
        using namespace ucl;
 
+       // MediaItem::Remover //
+
+       class MediaItem::Remover final : public BaseJob {
+       public:
+               friend class MediaItem::RemoverBuilder;
+               Remover(const NotiHandler &onComplete,
+                               const SharedRef<const MediaItems> &items) :
+                       BaseJob(onComplete, false),
+                       m_items(items)
+               {
+               }
+
+       protected:
+               // BaseJob //
+               virtual Result execute() final override
+               {
+                       MutexLock lock(getMediaMutex());
+
+                       for (auto &item: *m_items) {
+                               if (item) {
+                                       FAIL_RETURN(item->removeFile(),
+                                                       "item->removeFile() failed!");
+                               } else {
+                                       ELOG("item is NULL!");
+                               }
+                       }
+
+                       return RES_OK;
+               }
+
+       private:
+               const SharedRef<const MediaItems> m_items;
+       };
+
+       // MediaItem::RemoverBuilder //
+
+       MediaItem::RemoverBuilder &MediaItem::RemoverBuilder::
+                       setItems(MediaItems items)
+       {
+               m_items = makeShared<MediaItems>(std::move(items));
+               return *this;
+       }
+
+       IJobSRef MediaItem::RemoverBuilder::build(
+                       const NotiHandler &onComplete) const
+       {
+               if (!onComplete) {
+                       LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "onComplete is NULL!");
+               }
+               if (isEmpty(m_items)) {
+                       LOG_RETURN_VALUE(RES_INVALID_ARGUMENTS, {}, "Builder is empty!");
+               }
+
+               auto result = makeShared<Remover>(onComplete, m_items);
+
+               FAIL_RETURN_VALUE(result->prepare(), {}, "result->prepare() failed!");
+
+               return result;
+       }
+
+       // MediaItem //
+
        MediaItem::MediaItem(const MediaType type) :
                m_type(type),
                m_resolutionX(0),
@@ -157,16 +221,21 @@ namespace gallery {
                        LOG_RETURN(RES_FAIL, "File can't be removed!");
                }
 
-               const int ret = media_info_delete_from_db(m_mediaId.c_str());
-               if (ret != 0) {
-                       LOG_RETURN(RES_FAIL, "media_info_delete_from_db() failed: %d", ret);
-               }
+               {
+                       MutexLock lock(getMediaMutex());
 
-               freeMediaInfo();
-               m_isValid = false;
+                       const int ret = media_info_delete_from_db(m_mediaId.c_str());
+                       if (ret != 0) {
+                               LOG_RETURN(RES_FAIL, "media_info_delete_from_db() failed: %d", ret);
+                       }
+
+                       freeMediaInfo();
+                       m_isValid = false;
+               }
 
                if (!ecore_file_remove(m_filePath.c_str())) {
                        FLOG("ecore_file_remove() failed! Attemting to rescan....");
+                       MutexLock lock(getMediaMutex());
                        const int ret = media_content_scan_file(m_filePath.c_str());
                        if (ret != 0) {
                                ELOG("media_content_scan_file() failed: %d", ret);
@@ -195,10 +264,14 @@ namespace gallery {
 
                auto cbProxy = util::makeUnique(new ThumbCbProxy{this, cb});
 
-               const int ret = media_info_create_thumbnail(m_media,
-                       CALLBACK_B(ThumbCbProxy::completeCb), cbProxy.get());
-               if (ret != 0) {
-                       LOG_RETURN(RES_FAIL, "media_info_clone() failed: %d", ret);
+               {
+                       MutexLock lock(getMediaMutex());
+
+                       const int ret = media_info_create_thumbnail(m_media,
+                               CALLBACK_B(ThumbCbProxy::completeCb), cbProxy.get());
+                       if (ret != 0) {
+                               LOG_RETURN(RES_FAIL, "media_info_clone() failed: %d", ret);
+                       }
                }
 
                m_thumbCbProxy = std::move(cbProxy);
@@ -212,6 +285,8 @@ namespace gallery {
                        return;
                }
 
+               MutexLock lock(getMediaMutex());
+
                const int ret = media_info_cancel_thumbnail(m_media);
                if (ret != 0) {
 
index fe340d0a8548194a4bcbadbb3e952263be327482..387a5bc8e65f62f011869bd4fa8e9d3dd31e68c3 100644 (file)
@@ -20,6 +20,8 @@
 
 namespace gallery {
 
+       using namespace ucl;
+
        bool getProperty(media_info_h media,
                        int (*get)(media_info_h media, char **value),
                        std::string &result, const bool optional)
@@ -52,4 +54,10 @@ namespace gallery {
                }
                return true;
        }
+
+       Mutex &getMediaMutex()
+       {
+               static Mutex mutex{true};
+               return mutex;
+       }
 }
index dc41e6870cc83d78f551b7e0e629ee503787ad95..ad3c1ef54003da43be5f6eea59523b2a53262f59 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef __GALLERY_MODEL_HELPERS_H__
 #define __GALLERY_MODEL_HELPERS_H__
 
+#include "ucl/util/threading.h"
+
 #include "model/types.h"
 
 namespace gallery {
@@ -30,6 +32,8 @@ namespace gallery {
        bool getProperty(image_meta_h imageMeta,
                        int (*get)(image_meta_h imageMeta, int *value),
                        int &result, bool mayBeZero = false);
+
+       ucl::Mutex &getMediaMutex();
 }
 
 #include "helpers.hpp"
index 85f1e4a9082be292b8a97c72b03b4e3ecd392ffd..1fa4de6edf48553194da52f058e8821f363056e6 100644 (file)
@@ -53,7 +53,8 @@ namespace gallery {
        // ProcessingPresenter::Builder //
 
        ProcessingPresenter::Builder::Builder() :
-               m_iconType(IconType::NONE)
+               m_iconType(IconType::NONE),
+               m_forceProgress(false)
        {
        }
 
@@ -78,13 +79,20 @@ namespace gallery {
                return *this;
        }
 
+       ProcessingPresenter::Builder &ProcessingPresenter::Builder::
+                       setForceProgress(const bool value)
+       {
+               m_forceProgress = value;
+               return *this;
+       }
+
        ProcessingPresenterSRef ProcessingPresenter::Builder::
                        build(Widget &parent) const
        {
                auto result = makeShared<ProcessingPresenter>(m_iconType);
 
                FAIL_RETURN_VALUE(result->prepare(parent,
-                               m_processingText, m_completeText), {},
+                               m_processingText, m_completeText, m_forceProgress), {},
                                "result->prepare() failed!");
 
                return result;
@@ -111,7 +119,8 @@ namespace gallery {
 
        Result ProcessingPresenter::prepare(Widget &parent,
                        const TString &processingText,
-                       const TString &completeText)
+                       const TString &completeText,
+                       const bool forceProgress)
        {
                FAIL_RETURN(createWidget(parent, processingText),
                                "createWidget() failed!");
@@ -124,7 +133,9 @@ namespace gallery {
                        FAIL_RETURN(createIcon(), "createIcon() failed!");
                }
 
-               if (!resetTimer(impl::IDLE_WAIT_TIME_SEC)) {
+               if (forceProgress) {
+                       showProgress();
+               } else if (!resetTimer(impl::IDLE_WAIT_TIME_SEC)) {
                        LOG_RETURN(RES_FAIL, "resetTimer() failed!");
                }
 
@@ -218,6 +229,16 @@ namespace gallery {
                }
        }
 
+       void ProcessingPresenter::showProgress()
+       {
+               m_widget->emit(impl::SIGNAL_SHOW);
+
+               m_state = State::PROCESSING;
+               m_mayComplete = false;
+
+               resetTimer(impl::PROCESSING_MIN_TIME_SEC);
+       }
+
        void ProcessingPresenter::tryShowPopup()
        {
                if (m_isComplete && m_mayComplete && m_popup) {
@@ -259,10 +280,7 @@ namespace gallery {
 
                switch (m_state) {
                case State::WAITING:
-                       m_widget->emit(impl::SIGNAL_SHOW);
-                       m_state = State::PROCESSING;
-                       m_mayComplete = false;
-                       resetTimer(impl::PROCESSING_MIN_TIME_SEC);
+                       showProgress();
                        break;
 
                case State::PROCESSING:
index a9003b32586ba4f59c8d012f54b31c90302cb400..9510560b08e6fbdd06e1ee089037409f15626861 100644 (file)
@@ -736,7 +736,8 @@ namespace gallery {
                        return RES_FAIL;
                }
 
-               m_scrollLockIndex = (m_scrollOffset / m_slotSize);
+               m_scrollLockIndex = std::min(
+                               (m_scrollOffset / m_slotSize), (m_itemCount - 1));
 
                m_isInSelectMode = enabled;
 
@@ -1278,7 +1279,7 @@ namespace gallery {
                if (newScrollOffset != m_scrollOffset) {
                        DLOG("newScrollOffset: %d; m_scrollOffset: %d;",
                                        newScrollOffset, m_scrollOffset);
-                       m_scrollOffset= newScrollOffset;
+                       m_scrollOffset = newScrollOffset;
                        return true;
                }
 
index a6280cc1d2ad9c4ec34ad7293a00def4f933ed12..e1951bc49a26386ee9701ded03a560ef9a5dd75b 100644 (file)
@@ -27,7 +27,7 @@ namespace ucl {
 
        class Mutex : public NonCopyable {
        public:
-               Mutex();
+               Mutex(bool recursive = false);
                ~Mutex();
                void lock();
                void unlock();
index d152df35e8f1bad33943abe6ca5014e22210eab3..c3b75be3bcea8d09a5c2fbbb27fa998d8d1c34a7 100644 (file)
 
 namespace ucl {
 
-       inline Mutex::Mutex() :
+       inline Mutex::Mutex(const bool recursive) :
                m_mutex()
        {
-               pthread_mutex_init(&m_mutex, NULL);
+               if (recursive) {
+                       pthread_mutexattr_t attr = {};
+                       pthread_mutexattr_init(&attr);
+                       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+                       pthread_mutex_init(&m_mutex, &attr);
+                       pthread_mutexattr_destroy(&attr);
+               } else {
+                       pthread_mutex_init(&m_mutex, nullptr);
+               }
        }
 
        inline Mutex::~Mutex()