TizenRefApp-8196 Implement controller for input of recipients 80/120380/3
authorDenis Dolzhenko <d.dolzhenko@samsung.com>
Wed, 22 Mar 2017 13:21:51 +0000 (15:21 +0200)
committerDenis Dolzhenko <d.dolzhenko@samsung.com>
Wed, 22 Mar 2017 13:34:48 +0000 (15:34 +0200)
Move model of Recipient to separated class.

Change-Id: I8b017a1fcdd3bc81583c31410cee554b8ced9eb9
Signed-off-by: Denis Dolzhenko <d.dolzhenko@samsung.com>
.cproject
src/Common/Recipient/inc/Recipient.h [new file with mode: 0644]
src/Common/Recipient/src/Recipient.cpp [new file with mode: 0644]
src/Composer/Controller/inc/RecipFrame.h
src/Composer/Controller/inc/RecipInputFrame.h
src/Composer/Controller/src/RecipFrame.cpp
src/Composer/Controller/src/RecipInputFrame.cpp
src/Composer/View/inc/RecipLayout.h

index 24dae47d4916a3757cdd6a712ad01df16d5caee3..f59de09e15d6ab888d1cd42f74fe643f92b40f61 100644 (file)
--- a/.cproject
+++ b/.cproject
@@ -22,7 +22,7 @@
                                                        <targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="org.tizen.nativeide.target.sbi.gnu.platform.base.1782869252" osList="linux,win32" superClass="org.tizen.nativeide.target.sbi.gnu.platform.base"/>
                                                        <builder autoBuildTarget="all" buildPath="${workspace_loc:/message}/Debug" enableAutoBuild="true" id="org.tizen.nativecore.target.sbi.gnu.builder.166727839" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Tizen Application Builder" superClass="org.tizen.nativecore.target.sbi.gnu.builder"/>
                                                        <tool id="org.tizen.nativecore.tool.sbi.gnu.archiver.1041744234" name="Archiver" superClass="org.tizen.nativecore.tool.sbi.gnu.archiver"/>
-                                                       <tool command="clang++" id="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler.676364337" name="C++ Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler">
+                                                       <tool command="clang++" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" id="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler.676364337" name="C++ Compiler" superClass="org.tizen.nativecore.tool.sbi.gnu.cpp.compiler">
                                                                <option id="gnu.cpp.compiler.option.optimization.level.1792567016" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
                                                                <option defaultValue="gnu.cpp.compiler.debugging.level.max" id="sbi.gnu.cpp.compiler.option.debugging.level.core.721225116" name="Debug level" superClass="sbi.gnu.cpp.compiler.option.debugging.level.core" useByScannerDiscovery="false" valueType="enumerated"/>
                                                                <option defaultValue="false" id="sbi.gnu.cpp.compiler.option.misc.pic.core.2126605510" name="-fPIC option" superClass="sbi.gnu.cpp.compiler.option.misc.pic.core" useByScannerDiscovery="false" valueType="boolean"/>
                                                                        <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
                                                                </option>
                                                                <option id="gnu.cpp.compiler.option.include.paths.2037910591" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/Recipient/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Conversation/Main/Controller/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Composer/View/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Conversation/ConvList/Controller/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
                                                                </option>
                                                                <option id="gnu.cpp.compiler.option.include.paths.1895069727" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/Recipient/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Conversation/Main/Controller/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Composer/View/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Composer/Controller/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/Controller/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/AppControl/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/ContactManager/inc}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Conversation/ConvList/View/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/MsgEngine/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/SystemSettingsManager/inc}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Common/Utils/inc}&quot;"/>
diff --git a/src/Common/Recipient/inc/Recipient.h b/src/Common/Recipient/inc/Recipient.h
new file mode 100644 (file)
index 0000000..585f354
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016  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 Recipient_h_
+#define Recipient_h_
+
+#include <string>
+
+namespace Msg {
+    class Recipient {
+
+        public:
+            Recipient();
+            ~Recipient();
+
+            void clear();
+            const std::string &getAddress() const;
+            const std::string &getDispName() const;
+            void  setAddress(std::string address);
+            void  setDispName(std::string dispName);
+            bool isValid() const;
+
+            /**
+             * @brief       Search first valid recipient in ContactPersonNumber and ContactPersonPhoneLog based on a given search word.
+             * @param       searchWord - search keyword
+             * @return      recipient
+             */
+            static Recipient searchFirstRecip(const std::string &searchWord);
+
+        private:
+            std::string m_Address;
+            std::string m_DispName;
+    };
+}
+
+#endif /* RecipInputFrame_h_ */
diff --git a/src/Common/Recipient/src/Recipient.cpp b/src/Common/Recipient/src/Recipient.cpp
new file mode 100644 (file)
index 0000000..8486cba
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016  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 "Recipient.h"
+#include "App.h"
+#include "ContactManager.h"
+#include "Logger.h"
+#include "MsgUtils.h"
+
+#include <memory>
+
+using namespace Msg;
+
+namespace {
+    template<typename ContactRec>
+    std::shared_ptr<ContactRec> searchContact(const std::string &searchWord)
+    {
+        if (!searchWord.empty()) {
+            auto list = App::getInst().getContactManager().search<ContactRec>(searchWord);
+            if (list) {
+                do {
+                    auto &rec = list->get();
+                    if (rec.isValid() && MsgUtils::isValidAddress(rec.getAddress())) {
+                        return std::static_pointer_cast<ContactRec>(rec.clone());
+                    } else {
+                        MSG_LOG("Skip invalid contact: ", rec.getAddress());
+                    }
+                } while (list->next());
+             }
+        }
+        return {};
+    }
+}
+
+Recipient::Recipient()
+{
+}
+
+Recipient::~Recipient()
+{
+}
+
+void Recipient::clear()
+{
+    m_Address.clear();
+    m_DispName.clear();
+}
+
+const std::string &Recipient::getAddress() const
+{
+    return m_Address;
+}
+
+const std::string &Recipient::getDispName() const
+{
+    return m_DispName;
+}
+
+void Recipient::setAddress(std::string address)
+{
+    m_Address = std::move(address);
+}
+
+void Recipient::setDispName(std::string dispName)
+{
+    m_DispName = std::move(dispName);
+}
+
+bool Recipient::isValid() const
+{
+    return MsgUtils::isValidNumber(m_Address);
+}
+
+Recipient Recipient::searchFirstRecip(const std::string &searchWord)
+{
+    Recipient res;
+
+    auto numberRef = searchContact<ContactPersonNumber>(searchWord);
+
+    if (numberRef) {
+        res.m_Address = numberRef->getAddress();
+        res.m_DispName = numberRef->getDispName();
+    } else {
+        auto phoneLogRef = searchContact<ContactPersonPhoneLog>(searchWord);
+        if (phoneLogRef)
+            res.m_Address = phoneLogRef->getAddress();
+    }
+
+    if (res.m_DispName.empty())
+        res.m_DispName = res.m_Address;
+
+    return res;
+}
index a80acc676208bf5acd64a93d6faa6df066ac0e2e..4a9cc5bad5f55d0e32a1ba3747ef5e25d10b1a3f 100644 (file)
 
 #include "RecipFieldView.h"
 #include "FrameController.h"
-#include "DefaultLayout.h"
-#include "BottomButton.h"
-#include "RecipLayout.h"
-#include "RecipInputFrame.h"
+#include "Recipient.h"
 
 namespace Msg {
+
+    class RecipInputFrame;
+    class BottomButton;
+    class RecipLayout;
+    class DefaultLayout;
+
     class RecipFrame
         : public FrameController
         , private IRecipFieldViewListener {
@@ -48,13 +51,18 @@ namespace Msg {
             void onClearButtonClicked(RecipFieldView &obj) override;
             void onContactButtonClicked(RecipFieldView &obj) override;
 
+            // Entry(RecipField):
+            void onEntryChanged(Evas_Object *obj, void *event);
+
             void onInputFramePop(NaviFrameItem &item);
 
         private:
             void prepareLayouts();
             void prepareNextButton();
-            void prepareEntry();
+            void prepareRecipField();
             void showInputFrame();
+            void updateNextButton();
+            void updateRecipFieldButton();
 
         private:
             DefaultLayout *m_pBaseLayout;
@@ -62,6 +70,7 @@ namespace Msg {
             BottomButton *m_pNextButton;
             RecipFieldView *m_pRecipField;
             RecipInputFrame *m_pInputFrame;
+            Recipient m_Recip;
     };
 }
 
index 5d3fc484202d3bbfc2a5014b28583e93d18fef2c..b49bd1dbdccba03f25717c8fea27c7bceeb782ec 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "InputFrame.h"
 #include "SearchBar.h"
-#include "ContactAddress.h"
+#include "Recipient.h"
 
 namespace Msg {
 
@@ -28,14 +28,15 @@ namespace Msg {
         , private ISearchBarListener {
 
         public:
-            RecipInputFrame(NaviFrameController &parent, std::string recip);
+            RecipInputFrame(NaviFrameController &parent, const Recipient &recip);
             virtual ~RecipInputFrame();
 
-            std::string getAddress() const;
+            const Recipient &getRecip() const;
 
         private:
             // NaviFrameItem:
             void onAttached(ViewItem &item) override;
+            bool onRequestPop() override;
 
             // Entry:
             void onEntryChanged(Evas_Object *obj, void *event_info);
@@ -50,12 +51,10 @@ namespace Msg {
             Eina_Bool onSearchTimerTick();
 
         private:
-            template<typename ContactRec>
-            std::shared_ptr<ContactRec> search(const std::string &searchWord);
-
             void showSearchBar(bool show);
             void prepareEntry(std::string initText);
-            void close();
+            void setRecip(std::string address = {}, std::string dispName = {});
+            void close(std::string address = {}, std::string dispName = {});
             void reqestSearch();
             void cancelSearch();
             void searchButtonHandler();
@@ -63,8 +62,7 @@ namespace Msg {
         private:
             SearchBar *m_pSearchBar;
             Ecore_Timer *m_pTimer;
-            std::string m_ContactAddress;
-            std::string m_DsipName;
+            Recipient m_Recip;
     };
 }
 
index 8fe6bafb85ad35b4c2d528cd74b64a6485b12dfb..d59d0c0c2103a3123c156ef920abfe3cf91be991 100644 (file)
 #include "RecipFrame.h"
 #include "Callback.h"
 #include "ComposerFrame.h"
+#include "RecipInputFrame.h"
+#include "DefaultLayout.h"
+#include "BottomButton.h"
+#include "RecipLayout.h"
+#include "ToastPopup.h"
 
 using namespace Msg;
 
@@ -29,9 +34,13 @@ RecipFrame::RecipFrame(NaviFrameController &parent)
     , m_pInputFrame(nullptr)
 {
     MSG_LOG("");
+
     prepareLayouts();
     prepareNextButton();
-    prepareEntry();
+    prepareRecipField();
+
+    updateRecipFieldButton();
+    updateNextButton();
 }
 
 RecipFrame::~RecipFrame()
@@ -63,19 +72,33 @@ void RecipFrame::prepareNextButton()
     }
 }
 
-void RecipFrame::prepareEntry()
+void RecipFrame::prepareRecipField()
 {
     if (!m_pRecipField) {
         m_pRecipField = new RecipFieldView(*m_pLayout);
+        m_pRecipField->getEntry().addChangedCb(makeCbFirst(&RecipFrame::onEntryChanged), this);
         m_pRecipField->setListener(this);
-        m_pLayout->setEntry(*m_pRecipField);
+        m_pLayout->setRecipField(*m_pRecipField);
     }
 }
 
+void RecipFrame::updateNextButton()
+{
+    bool disabled = m_pRecipField->getEntry().isEmpty();
+    m_pNextButton->disabled(disabled);
+}
+
+void RecipFrame::updateRecipFieldButton()
+{
+    bool isEmpty = m_pRecipField->getEntry().isEmpty();
+    RecipFieldView::ButtonId buttonId = isEmpty ? RecipFieldView::ContactButtonId : RecipFieldView::ClearButtonId;
+    m_pRecipField->showButton(buttonId);
+}
+
 void RecipFrame::showInputFrame()
 {
     if (!m_pInputFrame) {
-        m_pInputFrame = new RecipInputFrame(getParent(), m_pRecipField->getEntry().getText());
+        m_pInputFrame = new RecipInputFrame(getParent(), m_Recip);
         m_pInputFrame->setOnPopCb(makeCbLast(&RecipFrame::onInputFramePop), this);
         getParent().push(*m_pInputFrame);
     }
@@ -97,8 +120,12 @@ void RecipFrame::onHwBackButtonPreessed(Evas_Object *obj, void *event_info)
 void RecipFrame::onNextButtonClicked(Evas_Object *obj, void *event)
 {
     MSG_LOG("");
-    auto *composerFrame = new ComposerFrame(getParent());
-    getParent().push(*composerFrame);
+    if (m_Recip.isValid()) {
+        auto *composerFrame = new ComposerFrame(getParent());
+        getParent().push(*composerFrame);
+    } else {
+        ToastPopup::toast("Invalid recipient."); // TODO: localization
+    }
 }
 
 void RecipFrame::onFieldClicked(RecipFieldView &obj)
@@ -110,16 +137,27 @@ void RecipFrame::onFieldClicked(RecipFieldView &obj)
 void RecipFrame::onClearButtonClicked(RecipFieldView &obj)
 {
     MSG_LOG("");
+    m_Recip.clear();
+    m_pRecipField->getEntry().clear();
 }
 
 void RecipFrame::onContactButtonClicked(RecipFieldView &obj)
 {
     MSG_LOG("");
+    // TODO: impl.
 }
 
 void RecipFrame::onInputFramePop(NaviFrameItem &item)
 {
     MSG_LOG("");
-    m_pRecipField->getEntry().setText((m_pInputFrame->getAddress()));
+    m_Recip = m_pInputFrame->getRecip();
+    m_pRecipField->getEntry().setText(m_Recip.getDispName());
     m_pInputFrame = nullptr;
 }
+
+void RecipFrame::onEntryChanged(Evas_Object *obj, void *event)
+{
+    MSG_LOG("");
+    updateNextButton();
+    updateRecipFieldButton();
+}
index 5cb14759e09a5923859104cc4ec217d80616889e..d5d48186e7350c802ad80380389d3a2098951f69 100644 (file)
  */
 
 #include "RecipInputFrame.h"
-#include "ContactManager.h"
 #include "Logger.h"
-#include "ContactPersonPhoneLog.h"
-#include "ContactPersonNumber.h"
 #include "App.h"
 #include "TextDecorator.h"
 #include "MsgUtils.h"
@@ -58,12 +55,13 @@ namespace {
     }
 }
 
-RecipInputFrame::RecipInputFrame(NaviFrameController &parent, std::string recip)
+RecipInputFrame::RecipInputFrame(NaviFrameController &parent, const Recipient &recip)
     : InputFrame(parent)
     , m_pSearchBar(nullptr)
     , m_pTimer(nullptr)
+    , m_Recip(recip)
 {
-    prepareEntry(std::move(recip));
+    prepareEntry(m_Recip.getAddress());
 }
 
 RecipInputFrame::~RecipInputFrame()
@@ -71,9 +69,9 @@ RecipInputFrame::~RecipInputFrame()
     cancelSearch();
 }
 
-std::string RecipInputFrame::getAddress() const
+const Recipient &RecipInputFrame::getRecip() const
 {
-    return getEntry().getText();
+    return m_Recip;
 }
 
 void RecipInputFrame::showSearchBar(bool show)
@@ -95,29 +93,26 @@ void RecipInputFrame::prepareEntry(std::string initText)
     getEntry().addMarkupFilterCb(makeCbFirst(&RecipInputFrame::onEntryFilter), this);
 }
 
-void RecipInputFrame::close()
+void RecipInputFrame::setRecip(std::string address, std::string dispName)
 {
-    cancelSearch();
-    pop();
+    if (!address.empty())
+       m_Recip.setAddress(std::move(address));
+
+   if (!dispName.empty())
+       m_Recip.setDispName(std::move(dispName));
+
+   if (m_Recip.getAddress().empty())
+       m_Recip.setAddress(getEntry().getText());
+
+   if (m_Recip.getDispName().empty())
+       m_Recip.setDispName(m_Recip.getAddress());
 }
 
-template<typename ContactRec>
-std::shared_ptr<ContactRec> RecipInputFrame::search(const std::string &searchWord)
+void RecipInputFrame::close(std::string address, std::string dispName)
 {
-    if (!searchWord.empty()) {
-        auto list = App::getInst().getContactManager().search<ContactRec>(searchWord);
-        if (list) {
-            do {
-                auto &rec = list->get();
-                if (rec.isValid() && MsgUtils::isValidAddress(rec.getAddress())) {
-                    return std::static_pointer_cast<ContactRec>(rec.clone());
-                } else {
-                    MSG_LOG("Skip invalid contact: ", rec.getAddress());
-                }
-            } while (list->next());
-         }
-    }
-    return {};
+    setRecip(std::move(address), std::move(dispName));
+    cancelSearch();
+    pop();
 }
 
 Eina_Bool RecipInputFrame::onSearchTimerTick()
@@ -130,37 +125,18 @@ Eina_Bool RecipInputFrame::onSearchTimerTick()
 
     if ((isDig && len >= minDigits)  || (!isDig && len >= minCharacters)) {
 
-        ContactPersonNumberRef numberRef;
-        ContactPersonPhoneLogRef phoneLogRef;
-        std::string address;
-        std::string dispName;
-
-        numberRef = search<ContactPersonNumber>(searchWord);
-
-        if (numberRef) {
-            address = numberRef->getAddress();
-            dispName = numberRef->getDispName();
-        } else if (!numberRef) {
-            phoneLogRef = search<ContactPersonPhoneLog>(searchWord);
-            if (phoneLogRef)
-                address = phoneLogRef->getAddress();
-        }
-
-        if (!dispName.empty()) {
-            highlightWord = TextDecorator::highlightKeyword(std::move(dispName), searchWord);
+        m_Recip = Recipient::searchFirstRecip(searchWord);
 
-        }
-        else if (!address.empty()) {
-            highlightWord = TextDecorator::highlightKeyword(std::move(address), searchWord);
-        }
+        if (!m_Recip.getDispName().empty())
+            highlightWord = TextDecorator::highlightKeyword(m_Recip.getDispName(), searchWord);
+        else if (!m_Recip.getAddress().empty())
+            highlightWord = TextDecorator::highlightKeyword(m_Recip.getAddress(), searchWord);
     }
 
-    if (!highlightWord.empty()) {
-        showSearchBar(true);
+    bool showHighlight = !highlightWord.empty();
+    showSearchBar(showHighlight);
+    if (showHighlight)
         m_pSearchBar->setText(highlightWord);
-    } else {
-        showSearchBar(false);
-    }
 
     m_pTimer = nullptr;
     return false; // Delete timer
@@ -206,10 +182,8 @@ void RecipInputFrame::searchButtonHandler()
             return;
         }
 
-        auto numberRef = search<ContactPersonNumber>(input);
-        auto phoneLogRef = search<ContactPersonPhoneLog>(input);
-
-        if (numberRef || phoneLogRef) {
+        Recipient recip = Recipient::searchFirstRecip(input);
+        if (recip.isValid()) {
             // TODO: impl.
         } else {
             if (MsgUtils::isValidNumber(input)) {
@@ -227,6 +201,13 @@ void RecipInputFrame::onAttached(ViewItem &item)
     InputFrame::onAttached(item);
 }
 
+bool RecipInputFrame::onRequestPop()
+{
+    cancelSearch();
+    setRecip();
+    return true;
+}
+
 void RecipInputFrame::onEntryFilter(Evas_Object *obj, char **text)
 {
     if (*text) {
index 3850dbfa99e115aeb17b4cd1c6708d06af999a8e..ab89aed2bbc268e0b0c2f15f02d048d1d0d5453d 100644 (file)
@@ -26,7 +26,7 @@ namespace Msg {
         public:
             RecipLayout(Evas_Object *parent);
 
-            void setEntry(Evas_Object *obj);
+            void setRecipField(Evas_Object *obj);
     };
 
     inline RecipLayout::RecipLayout(Evas_Object *parent)
@@ -34,7 +34,7 @@ namespace Msg {
         setEo(addLayout(parent, RECIP_LAYOUT_EDJ_PATH, "main"));
     }
 
-    inline void RecipLayout::setEntry(Evas_Object *obj)
+    inline void RecipLayout::setRecipField(Evas_Object *obj)
     {
         setContent(obj, "swl.entry");
     }