TizenRefApp-9082 [Call UI] Implement Screen Reader highlight logic for MainPage 24/143224/2
authorIgor Olshevskyi <i.olshevskyi@samsung.com>
Wed, 9 Aug 2017 04:57:03 +0000 (07:57 +0300)
committerIgor Olshevskyi <i.olshevskyi@samsung.com>
Fri, 11 Aug 2017 14:13:06 +0000 (17:13 +0300)
Change-Id: I91c20c3a111762ef110ce1ec132fd124920b63b7

38 files changed:
edc/accept_reject.edc
edc/call_info.edc
edc/main_ly.edc
edc/more_option.edc
edc/reject_msg.edc
edc/volume_control.edc
inc/config.h
inc/presenters/AcceptRejectPresenter.h
inc/presenters/AccessoryPresenter.h
inc/presenters/AtspiHighlightHelper.h [new file with mode: 0644]
inc/presenters/CallInfoPresenter.h
inc/presenters/CallStatusPresenter.h
inc/presenters/MainPage.h
inc/presenters/MoreOptionsPresenter.h
inc/presenters/RejectMsgPresenter.h
inc/presenters/types.h
inc/resources.h
inc/view/AcceptRejectWidget.h
inc/view/VolumeControl.h
inc/view/helpers.h
src/presenters/AcceptDialog.cpp
src/presenters/AcceptRejectPresenter.cpp
src/presenters/AccessoryPresenter.cpp
src/presenters/AtspiHighlightHelper.cpp [new file with mode: 0644]
src/presenters/CallInfoPresenter.cpp
src/presenters/CallStatusPresenter.cpp
src/presenters/Instance.cpp
src/presenters/MainPage.cpp
src/presenters/MoreOptionsPresenter.cpp
src/presenters/RejectMsgPresenter.cpp
src/presenters/common.h
src/presenters/helpers.cpp
src/presenters/helpers.h
src/resources.cpp
src/view/AcceptRejectWidget.cpp
src/view/Slider.cpp
src/view/VolumeControl.cpp
src/view/helpers.cpp

index 6938525da323ea34aeed31d238399f72b327b055..5b1d3600b4b505e9325377981d039b4bdd11e555 100644 (file)
@@ -73,7 +73,6 @@ group { "elm/layout/callui/event_accept_reject";
                rect { "bg";
                        scale;
                        mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
                                fixed: 1 1;
@@ -162,20 +161,8 @@ group { "elm/layout/callui/accept_reject_main";
 
                // Control parts
 
-               swallow { "swl.accept.finger.event";
-                       scale;
-                       mouse;
-                       desc { "default";
-                               min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
-                               fixed: 1 1;
-                               rel1 { relative: CU_ACCEPT_COMPONENT_REL; to: "bg"; }
-                               rel2 { relative: CU_ACCEPT_COMPONENT_REL; to: "bg"; }
-                       }
-               }
                swallow { "swl.accept.finger.guide.bg";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
                                fixed: 1 1;
@@ -185,8 +172,6 @@ group { "elm/layout/callui/accept_reject_main";
                }
                swallow { "swl.accept.finger.tracer";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_FINGER_TRACER_BG_SIZE;
                                fixed: 1 1;
@@ -196,8 +181,6 @@ group { "elm/layout/callui/accept_reject_main";
                }
                swallow { "swl.accept.icon";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_SIZE;
                                fixed: 1 1;
@@ -205,20 +188,18 @@ group { "elm/layout/callui/accept_reject_main";
                                rel2 { relative: CU_ACCEPT_COMPONENT_REL; to: "bg"; }
                        }
                }
-               swallow { "swl.reject.finger.event";
+               swallow { "swl.accept.finger.event";
                        scale;
                        mouse;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
                                fixed: 1 1;
-                               rel1 { relative: CU_REJECT_COMPONENT_REL; to: "bg"; }
-                               rel2 { relative: CU_REJECT_COMPONENT_REL; to: "bg"; }
+                               rel1 { relative: CU_ACCEPT_COMPONENT_REL; to: "bg"; }
+                               rel2 { relative: CU_ACCEPT_COMPONENT_REL; to: "bg"; }
                        }
                }
                swallow { "swl.reject.finger.guide.bg";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
                                fixed: 1 1;
@@ -228,8 +209,6 @@ group { "elm/layout/callui/accept_reject_main";
                }
                swallow { "swl.reject.finger.tracer";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_FINGER_TRACER_BG_SIZE;
                                fixed: 1 1;
@@ -239,8 +218,6 @@ group { "elm/layout/callui/accept_reject_main";
                }
                swallow { "swl.reject.icon";
                        scale;
-                       mouse;
-                       repeat;
                        desc { "default";
                                min: CU_ACCEPT_REJECT_ICON_SIZE;
                                fixed: 1 1;
@@ -248,5 +225,15 @@ group { "elm/layout/callui/accept_reject_main";
                                rel2 { relative: CU_REJECT_COMPONENT_REL; to: "bg"; }
                        }
                }
+               swallow { "swl.reject.finger.event";
+                       scale;
+                       mouse;
+                       desc { "default";
+                               min: CU_ACCEPT_REJECT_ICON_BG_SIZE;
+                               fixed: 1 1;
+                               rel1 { relative: CU_REJECT_COMPONENT_REL; to: "bg"; }
+                               rel2 { relative: CU_REJECT_COMPONENT_REL; to: "bg"; }
+                       }
+               }
        }
 }
index ccd688e86861be6fd427dbc9c3588df5976bf14e..86dc549fd43a83b334966732971b03c97e13ad64 100644 (file)
@@ -218,12 +218,17 @@ group { "elm/layout/callui/call_info";
                CU_DOT("dot.first", 3, -3)
                CU_DOT("dot.second", 9, -9)
                CU_DOT("dot.third", 15, -15)
+
                rect { "ao_text_info";
                        mouse;
                        scale;
                        desc { "default";
-                               rel1.to: "text_info";
-                               rel2.to: "text_info";
+                               fixed: 1 1;
+                               align: 0.0 0.0;
+                               min: 208 32;
+                               max: 208 32;
+                               rel1 { relative: 1.0 1.0; to_x: "left.pad";  to_y: "top.pad"; }
+                               rel2 { relative: 0.0 1.0; to_x: "right.pad"; to_y: "top.pad"; }
                                color: 0 0 0 0;
                        }
                }
index e87ac933678144e5500904056d58294f371dddeb..1e9dd3d6e7b929f66a1d9d6abef22e068080dc2d 100644 (file)
@@ -57,3 +57,7 @@ group { "elm/layout/callui/main";
                }
        }
 }
+
+group { "elm/layout/callui/fake_access_object";
+       data.item: "access_highlight" "on";
+}
index 56a657772d32b8341ae5deb5786416243165ae40..f5640cfdd1d7f0abea4f84c88d2b321cd62d58b7 100644 (file)
@@ -78,6 +78,15 @@ group { "elm/layout/callui/more_option";
                                text.fit: 1 1;
                        }
                }
+               rect { "ao_txt.status";
+                       mouse;
+                       scale;
+                       desc { "default";
+                               rel1.to: "txt.status";
+                               rel2.to: "txt.status";
+                               color: 0 0 0 0;
+                       }
+               }
                swallow { "swl.slot.1";
                        scale;
                        desc { "default";
index cc88b05b304601a94c87936f620badeddef8ad94..ef23efb9b72d32b0a6fb16c08fd948d79c105411 100644 (file)
@@ -43,7 +43,7 @@ group { "elm/layout/callui/reject_msg";
                        }
                }
                image { "cue";
-                       repeat;
+                       scale;
                        desc { "default";
                                align: 0.5 1.0;
                                fixed: 0 1;
@@ -74,8 +74,6 @@ group { "elm/layout/callui/reject_msg";
                }
                textblock { "reject_msg_text";
                        scale;
-                       nomouse;
-                       repeat;
                        desc { "default";
                                rel1 { relative: 0.0 0.0; to: "reject_msg_text_zone"; }
                                rel2 { relative: 1.0 1.0; to: "reject_msg_text_zone"; }
@@ -88,6 +86,15 @@ group { "elm/layout/callui/reject_msg";
                                hid;
                        }
                }
+               rect { "ao_cue";
+                       mouse;
+                       scale;
+                       desc { "default";
+                               rel1 { relative: 0.0 0.0; to: "reject_msg_text_zone"; }
+                               rel2 { relative: 1.0 1.0; to_x: "reject_msg_text_zone"; }
+                               color: 0 0 0 0;
+                       }
+               }
        }
 }
 
index e0ab578b42aa265b22c753784556e9ae68cc656c..21e173d33b95cea9e86789eb9ae9cc3590fa2696 100644 (file)
@@ -118,6 +118,18 @@ group { "elm/layout/callui/volume_control";
                                hid;
                        }
                }
+               textblock { "ao_txt.value";
+                       scale;
+                       mouse;
+                       desc { "default";
+                               fixed: 1 1;
+                               rel1.to: "txt.value";
+                               rel2.to: "txt.value";
+                       }
+                       desc { "hide";
+                               hid;
+                       }
+               }
                swallow { "swl.minus";
                        scale;
                        desc { "default";
index 5d9d1130b06924d062d5a1d30e89d93009333178..fd664e047520ab3f05f06ec93a8b5629ee845ee3 100644 (file)
 
 namespace callui {
 
+       constexpr auto PACKAGE = "w-call-ui";
+
        constexpr auto WINDOW_NAME = "org.tizen.call-ui";
 
        constexpr auto BASE_SCALE = 1.3;
 }
 
-#endif // __CALL_UI_CONFIG_H__
+#endif // __CALLUI_CONFIG_H__
index fd477e9f07dc86455877ef052ce3af4bde124b3a..49ad4e13cb8d69019125a7b190d138993bb4393e 100644 (file)
@@ -52,6 +52,9 @@ namespace callui {
 
                void update(CallMask calls);
 
+               ucl::ElmWidget *getAcceptAo();
+               ucl::ElmWidget *getRejectAo();
+
        private:
                friend class ucl::ReffedObj<AcceptRejectPresenter>;
                AcceptRejectPresenter(ucl::IRefCountObj &rc,
index 1cd131afcf2854a7f3d42dedf3eeb6ccd20daa64..2a3d5735aba774d1adc1eb8fa47d92b3645ba8aa 100644 (file)
@@ -53,6 +53,16 @@ namespace callui {
                void hideVolumeControls();
                ucl::Result update(const ICallManagerSRef &cm);
 
+               // Screen Reader
+               ucl::ElmWidget *getVolumBtn();
+               ucl::ElmWidget *getBluetoothBtn();
+               ucl::ElmWidget *getMuteBtn();
+               ucl::ElmWidget *getAddContactBtn();
+               ucl::ElmWidget *getVolumeControlLy();
+               ucl::ElmWidget *getVolumeControlDecreaseBtn();
+               ucl::ElmWidget *getVolumeControlIncreaseBtn();
+               ucl::ElmWidget *getVolumeControlValueTxtAo();
+
        private:
                enum class ComponentsMode {
                        UNDEFINED,
@@ -90,6 +100,8 @@ namespace callui {
                void onVolumeControlEventCb(VolumeControlEvent event);
                Eina_Bool onRotaryEvent(Eext_Rotary_Event_Info *info);
 
+               bool checkPossibilityToModifyVolume(int volume,
+                               bool needIncrease);
                void tryIncreaseVolume();
                void tryDecreaseVolume();
 
@@ -113,6 +125,10 @@ namespace callui {
                ucl::Result setActiveCallCompomnents();
                ucl::Result setEndCallCompomnents(const ICallManagerSRef &cm);
 
+               // Screen Reader
+               void registerVolumeControlAo();
+               void onVolumeControlScreenReaderReadStart(ucl::Widget &widget, void *eventInfo);
+
        private:
                ucl::LayoutSRef m_widget;
                ucl::StyledWidgetSRef m_volumeBtn;
@@ -129,9 +145,9 @@ namespace callui {
                ComponentsMode m_mode;
                std::string m_unsavedPhoneNumber;
                NotiHandler m_exitHandler;
+
+               bool m_isVcShowOnRotaryEvent;
        };
 }
 
-
-
 #endif // __CALLUI_PRESENTERS_ACCESSORY_PRESENTER_H__
diff --git a/inc/presenters/AtspiHighlightHelper.h b/inc/presenters/AtspiHighlightHelper.h
new file mode 100644 (file)
index 0000000..e6d127d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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 __CALLUI_PRESENTERS_ATSPI_HIGHLIGHT_HELPER_H__
+#define __CALLUI_PRESENTERS_ATSPI_HIGHLIGHT_HELPER_H__
+
+#include "ucl/mvp/GuiPresenter.h"
+
+#include "types.h"
+
+namespace callui {
+
+       class AtspiHighlightHelper final : public ucl::GuiPresenter {
+       public:
+               using RelationEventHandler = ucl::WeakDelegate<Elm_Interface_Atspi_Accessible *(
+                               Elm_Interface_Atspi_Accessible *ao,
+                               Elm_Atspi_Relation_Type flowRelation)>;
+
+               using GestureEventHandler = ucl::WeakDelegate<bool (
+                               Elm_Interface_Atspi_Accessible *ao,
+                               Elm_Atspi_Gesture_Type gestureType)>;
+       public:
+               static AtspiHighlightHelperSRef newInstance(GuiPresenter &parent,
+                               ucl::ElmWidget &rootWidget);
+
+               void setRelationEventHandler(RelationEventHandler handler);
+               void setGestureEventHandler(GestureEventHandler handler);
+               void registerWidget(ucl::ElmWidget &widget);
+               bool handleGesture(Elm_Interface_Atspi_Accessible *widget,
+                               const Elm_Atspi_Gesture_Info &info);
+
+       private:
+               friend class ucl::ReffedObj<AtspiHighlightHelper>;
+               AtspiHighlightHelper(ucl::IRefCountObj &rc);
+               virtual ~AtspiHighlightHelper();
+
+               ucl::Result prepare(GuiPresenter &parent, ucl::ElmWidget &rootWidget);
+
+               void handleAtspiGesture(Elm_Interface_Atspi_Accessible *widget,
+                               ucl::AtspiGestureEventInfo &e);
+
+       private:
+               void onAtspiGesture(ucl::Widget &widget, void *eventInfo);
+
+       private:
+               RelationEventHandler m_relationEventHandler;
+               GestureEventHandler m_gestureEventHandler;
+       };
+}
+
+#endif // __CALLUI_PRESENTERS_ATSPI_HIGHLIGHT_HELPER_H__
index c7bc82c5f0452082fc6760888ca957e7ad9dbccd..b2e51da9ac290e9b94d4ab9a7f0fbb4be7081a40 100644 (file)
@@ -49,6 +49,11 @@ namespace callui {
                CallMode getMode() const;
                ucl::Result update(CallMode mode, const ICallManagerSRef &cm);
 
+               // Screen Reader
+               ucl::ElmWidget *getStatusTxtAo();
+               ucl::ElmWidget *getMainTxtAo();
+               ucl::ElmWidget *getSubTxtAo();
+
        private:
                friend class ucl::ReffedObj<CallInfoPresenter>;
                CallInfoPresenter(ucl::IRefCountObj &rc,
@@ -69,7 +74,6 @@ namespace callui {
                ucl::Result updateCallerId();
                ucl::Result updateSubText();
                ucl::Result updateMainTxt();
-
                std::string getNumberSubText(const ICallInfoSCRef &callInfo) const;
                std::string getIncomingCallSubText() const;
                std::string getOutgoingCallSubText() const;
@@ -78,7 +82,12 @@ namespace callui {
 
                std::string generateMainTxt(const ICallInfoSCRef &callInfo);
 
-               void displayMainTxt(const ICallInfoSCRef &info, const std::string &text) const;
+               void displayMainTxt(const ICallInfoSCRef &info, const std::string &text);
+
+               // Screen Reader
+               ucl::Result setMainTxtAccessObject(const std::string &text);
+               ucl::Result setSubTxtAccessObject(const std::string &text);
+
        private:
                ucl::LayoutSRef m_widget;
                ucl::StyledWidgetSRef m_callerId;
@@ -92,6 +101,11 @@ namespace callui {
                CallStatusPresenterSRef m_callStatus;
                bool m_isSubTxtEnable;
                bool m_needModifyCallStatus;
+
+               // Screen Reader
+               ucl::ElmWidgetSRef m_statusAO;
+               ucl::ElmWidgetSRef m_mainTxtAO;
+               ucl::ElmWidgetSRef m_subTxtAO;
        };
 
 }
index 0f788b3aafd0696842b6fae442c88d3283d3ec00..c1d5a304e01575d101e1c5bd33150d16dbe47674 100644 (file)
@@ -47,6 +47,9 @@ namespace callui {
        public:
                virtual ~CallStatusPresenter();
 
+               // Screen Reader
+               ucl::ElmWidget *getStatusTextAo();
+
        private:
                friend class ucl::ReffedObj<CallStatusPresenter>;
                CallStatusPresenter(ucl::IRefCountObj &rc,
@@ -65,6 +68,9 @@ namespace callui {
                Eina_Bool onCallDurationTimerCb();
                Eina_Bool onBlinkingTimerCb();
 
+               // Screen Reader
+               ucl::Result createStatusTxtAo();
+
        private:
                ucl::LayoutSRef m_ly;
                CallMode m_mode;
@@ -73,6 +79,9 @@ namespace callui {
                Ecore_Timer *m_timer;
                struct tm m_duration;
                int m_blinkCount;
+
+               // Screen Reader
+               ucl::ElmWidgetSRef m_statusTxtAo;
        };
 
 }
index 6366dea9a34b3e7d23cbf81d10a887691dceafb3..03747c0e300dd09405f79d7808e7139bbb67b9cb 100644 (file)
@@ -67,10 +67,12 @@ namespace callui {
                ucl::Result createRejectMsgPresenter(
                                const IRejectMsgProviderSRef &provider);
 
+               ucl::Result createRejectMsgCue();
                void RejectMsgStateCb(RejectMsgState state);
                void RejectMsgSelectCb(const IRejectMsgSRef &rm);
 
-               ucl::Result createBottomBtn(const ucl::ElmStyle &style);
+               ucl::Result createBottomBtn(const ucl::ElmStyle &style,
+                               bool setVisible = true);
                void onBottomBtnClicked(ucl::Widget &widget, void *eventInfo);
 
                void startEndCallTimer();
@@ -80,8 +82,6 @@ namespace callui {
                void onPowerKeyUp(ucl::Widget &widget, void *eventInfo);
                void processKeyPress();
 
-               bool detectMuteControlDisableState();
-
                ucl::Result updateDeviceState(CallMode prevMode, CallMode curMode);
 
                ucl::Result createWidget();
@@ -90,6 +90,25 @@ namespace callui {
 
                void onExitAppRequest();
 
+               // Screen Reader
+               ucl::Result createRejectMsgCueAo();
+               ucl::Result createAtspiHighlightHelper();
+               void registerIncomingCallModeAo();
+               void registerActiveCallModeAo();
+               void registerEndCallModeAo();
+               Elm_Interface_Atspi_Accessible *onIncomingCallModeAtspiHighlight(
+                               Elm_Interface_Atspi_Accessible *widget,
+                               Elm_Atspi_Relation_Type flowRelation);
+               Elm_Interface_Atspi_Accessible *onActiveCallModeAtspiHighlight(
+                               Elm_Interface_Atspi_Accessible *widget,
+                               Elm_Atspi_Relation_Type flowRelation);
+               Elm_Interface_Atspi_Accessible *onEndCallModeAtspiHighlight(
+                               Elm_Interface_Atspi_Accessible *ao,
+                               Elm_Atspi_Relation_Type flowRelation);
+               bool onIncomingModeAtspiGesture(
+                               Elm_Interface_Atspi_Accessible *widget,
+                               Elm_Atspi_Gesture_Type gestureType);
+
                // Page
 
                virtual void onBackKey() final override;
@@ -117,6 +136,10 @@ namespace callui {
                CallMode m_mode;
                Ecore_Timer *m_ecTimer;
                bool m_ecTimerBtnReq;
+
+               // Screen Reader
+               ucl::ElmWidgetSRef m_rmCueAo;
+               AtspiHighlightHelperSRef m_atspiHelper;
        };
 }
 
index a4f4a13f5011e117712050e49f11d74cbc9a21bc..e1b62ddc910d98d7a11449079d260de43c7db27f 100644 (file)
@@ -39,6 +39,7 @@ namespace callui {
                        Builder &setNaviframe(const ucl::NaviframeSRef &navi);
                        Builder &setParentWidget(const ucl::ElmWidgetSRef &parentWidget);
                        MoreOptionsPresenterSRef build(ucl::GuiPresenter &parent) const;
+
                private:
                        ICallManagerSRef m_cm;
                        ISoundManagerSRef m_sm;
@@ -50,6 +51,9 @@ namespace callui {
                ucl::Widget &getWidget();
                void update();
 
+               // Screen Reader
+               ucl::ElmWidget *getCueAo();
+
        private:
                friend class ucl::ReffedObj<MoreOptionsPresenter>;
                MoreOptionsPresenter(ucl::IRefCountObj &rc,
@@ -61,10 +65,16 @@ namespace callui {
                ucl::Result prepare(ucl::GuiPresenter &parent,
                                ucl::ElmWidget &parentWidget);
 
+               void updateComponents();
+
                ucl::Result createWidget(ucl::ElmWidget &parent);
                ucl::Result createPanel();
                ucl::Result createPanelLayout();
-               ucl::Result createButtons();
+
+               ucl::Result createSwapButton();
+               ucl::Result createUnholdButton();
+               ucl::Result createKeypadButton();
+
                ucl::StyledWidgetSRef createButton(const ucl::ElmStyle &style,
                                const ucl::TString &txt,
                                const ucl::WidgetEventHandler &handler);
@@ -84,6 +94,9 @@ namespace callui {
                void onPanelInactivate(Evas_Object *obj,
                                const char *emission,
                                const char *source);
+               void onCueClicked(Evas_Object *obj,
+                               const char *emission,
+                               const char *source);
 
                ucl::Result startCallDurationTimer();
                void stopCallDurationTimer();
@@ -95,6 +108,18 @@ namespace callui {
 
                void onPageExitRequest(Page &page);
 
+               // Screen Reader
+               ucl::Result createAccessObjects();
+               ucl::Result createCueAo();
+               ucl::Result createStatusTxtAo();
+               ucl::Result createFakeAo();
+               ucl::Result createAtspiHighlightHelper();
+               Elm_Interface_Atspi_Accessible *onAtspiHighlight(
+                               Elm_Interface_Atspi_Accessible *ao,
+                               Elm_Atspi_Relation_Type flowRelation);
+               Eina_Bool onCueAoActionCb(Evas_Object *obj,
+                               Elm_Access_Action_Info *actionInfo);
+
        private:
                ucl::LayoutSRef m_widget;
                ucl::StyledWidgetSRef m_panel;
@@ -102,16 +127,21 @@ namespace callui {
                ucl::StyledWidgetSRef m_btnSwap;
                ucl::StyledWidgetSRef m_btnUnhold;
                ucl::StyledWidgetSRef m_btnKeypad;
-               PageWRef m_keypad;
 
+               PageWRef m_keypad;
                ICallManagerSRef m_cm;
                ISoundManagerSRef m_sm;
                ucl::NaviframeSRef m_navi;
-
                ICallInfoWCRef m_info;
 
                Ecore_Timer *m_timer;
                struct tm m_duration;
+
+               // Screen Reader
+               AtspiHighlightHelperSRef m_atspiHelper;
+               ucl::ElmWidgetSRef m_fakeAo;
+               ucl::ElmWidgetSRef m_cueAo;
+               ucl::ElmWidgetSRef m_statusTxtAo;
        };
 }
 
index 31740fbc05011fc5c602b2193b2c9f20ff54fa92..d04558a98ac88488783f09fc0a9bad4257706705 100644 (file)
@@ -52,6 +52,7 @@ namespace callui {
 
                RejectMsgState getState();
 
+               void showPanel();
                void hidePanel();
 
                void setStateHandler(const RejectMsgStateHandler &handler);
@@ -75,8 +76,8 @@ namespace callui {
                ucl::Result createPanelBg();
                ucl::Result createPanelLy();
                ucl::Result createGenlist();
-               ucl::Result fillGenlist();
 
+               ucl::Result fillGenlist();
                ucl::Result addGenlistTitleItem();
                ucl::Result addGenlistTextItem(const IRejectMsgSRef &rm);
                ucl::Result addGenlistBottomItem();
@@ -91,6 +92,18 @@ namespace callui {
 
                void onBackKey(Evas_Object *obj, void *eventInfo);
 
+               // Screen Reader
+               ucl::Result createAtspiHighlightHelper();
+               void registerGenlistAtspiGestureCallbacks();
+               Elm_Interface_Atspi_Accessible *getFirstAo();
+               Elm_Interface_Atspi_Accessible *getLastAo();
+               Eina_Bool onAtspiGesture(
+                               Elm_Atspi_Gesture_Info gestureInfo,
+                               Elm_Interface_Atspi_Accessible *ao);
+               Elm_Interface_Atspi_Accessible *onAtspiHighlight(
+                               Elm_Interface_Atspi_Accessible *ao,
+                               Elm_Atspi_Relation_Type flowRelation);
+
                // Presenter
 
                virtual void onActivate() final override;
@@ -109,6 +122,8 @@ namespace callui {
                RejectMsgSelectHandler m_selectHandler;
                RejectMsgState m_state;
 
+               // Screen Reader
+               AtspiHighlightHelperSRef m_atspiHelper;
        };
 
 }
index eea785ec4594fbd0fb3a0c5ee92c3eb377755c4b..e2dadb95034279da791801fcceb63d36b1d87e1f 100644 (file)
@@ -79,6 +79,8 @@ namespace callui {
        UCL_DECLARE_REF_ALIASES(DeviceStatePresenter);
        UCL_DECLARE_REF_ALIASES(MotionSensorPresenter);
 
+       UCL_DECLARE_REF_ALIASES(AtspiHighlightHelper);
+
        using AcceptDialogHandler = ucl::WeakDelegate<bool(AcceptDialog &, AcceptDialogEvent)>;
        using RejectMsgStateHandler = ucl::WeakDelegate<void(RejectMsgState)>;
        using RejectMsgSelectHandler = ucl::WeakDelegate<void(const IRejectMsgSRef &rm)>;
index a6983eeb31361ec717e7c5fe82e46b0e470fccad..cbc2b05db62d147a6946ede0e6b9e3af452fd2a1 100644 (file)
@@ -54,6 +54,27 @@ namespace callui {
        extern const ucl::TString STR_MORE_UNHOLD;
        extern const ucl::TString STR_MORE_TRANSFER;
        extern const ucl::TString STR_MORE_GEAR;
+
+       // Screen Reader
+       extern const ucl::TString AO_STR_CALL;
+       extern const ucl::TString AO_STR_VOLUME;
+       extern const ucl::TString AO_STR_HEADSET;
+       extern const ucl::TString AO_STR_GEAR_SPK;
+       extern const ucl::TString AO_STR_MUTE;
+       extern const ucl::TString AO_STR_MORE_OPTIONS;
+       extern const ucl::TString AO_STR_END_CALL;
+       extern const ucl::TString AO_STR_CALLBACK;
+       extern const ucl::TString AO_STR_ADD_TO_CONTACTS;
+       extern const ucl::TString AO_STR_ROTATE_BEZEL_TO_ADJUST;
+       extern const ucl::TString AO_STR_DECREASE_VOLUME;
+       extern const ucl::TString AO_STR_INCREASE_VOLUME;
+       extern const ucl::TString AO_STR_ACCEPT_CALL;
+       extern const ucl::TString AO_STR_SWIPE_RIGHT_WITH_TWO_FINGERS_TO_ACCEPT;
+       extern const ucl::TString AO_STR_REJECT_CALL;
+       extern const ucl::TString AO_STR_SWIPE_LEFT_WITH_TWO_FINGERS_TO_REJECT;
+       extern const ucl::TString AO_STR_DECLINE_MESSAGES;
+       extern const ucl::TString AO_STR_SWIPE_UP_WITH_TWO_FINGERS_TO_SEND_A_DECLINE_MESSAGE;
+
 }
 
 #endif // __CALLUI_RESOURCES_H__
index bd7865b5e96ee5285f3a794e5ba1b98d011ed12f..a3040475ea785c70fa94d625089c616359c06e7a 100644 (file)
@@ -52,6 +52,10 @@ namespace callui {
                void deactivateRotary();
                void setAcceptBtnType(AcceptButtonType type);
 
+               // Screen Reader
+               ucl::ElmWidget *getAcceptAo();
+               ucl::ElmWidget *getRejectAo();
+
        private:
                friend class ucl::ReffedObj<AcceptRejectWidget>;
                AcceptRejectWidget(ucl::IRefCountObj &rc,
@@ -125,6 +129,10 @@ namespace callui {
                void setAcceptUnpressedState();
                void setRejectUnpressedState();
 
+               // Screen Reader
+               ucl::Result registerAccessObjects(
+                               ucl::ElmWidget &widget);
+
        private:
                ucl::Layout *m_layout;
                NotiHandler m_accHandler;
@@ -173,6 +181,10 @@ namespace callui {
                int m_rejBCAnimIndex;
 
                AcceptButtonType m_acceptBtnType;
+
+               // Screen Reader
+               ucl::ElmWidgetSRef m_accAo;
+               ucl::ElmWidgetSRef m_rejAo;
        };
 
 }
index d8299d403b1aadd711257fbda03fc489be141537..98fbccbdcde2c36504402cfaf9442a49ed370970 100644 (file)
@@ -54,6 +54,11 @@ namespace callui {
 
                virtual void setValue(int value) override final;
 
+               // Screen Reader
+               ucl::ElmWidget *getDecreaseBtn();
+               ucl::ElmWidget *getIncreaseBtn();
+               ucl::ElmWidget *getValueTxtAo();
+
        private:
                friend class ucl::ReffedObj<VolumeControl>;
                VolumeControl(ucl::IRefCountObj &rc,
@@ -70,10 +75,19 @@ namespace callui {
                void onDecreaseBtnClickedCb(ucl::Widget &widget, void *eventInfo);
                void onIncreaseBtnClickedCb(ucl::Widget &widget, void *eventInfo);
 
+               void onWidgetShowCb(Widget &widget, void *eventInfo);
+               void onWidgetHideCb(Widget &widget, void *eventInfo);
+
+               // Screen Reader
+               void registerAccessObjectInformation();
+
        private:
                ucl::StyledWidget m_decreaseBtn;
                ucl::StyledWidget m_increaseBtn;
                VolumeControlEventHandler m_handler;
+
+               // Screen Reader
+               ucl::ElmWidgetSRef m_valueTxtAo;
        };
 
 }
index d0d3395e82fc76de910ab16afcb8599f37800f6a..4ebd3d7c05924efbd47aa6781542276ad4e91b45 100644 (file)
 
 #include <efl_extension.h>
 
-#include "types.h"
-
-namespace ucl {
+#include "ucl/gui/ElmWidget.h"
+#include "ucl/gui/Naviframe.h"
+#include "ucl/gui/Layout.h"
 
-       class ElmWidget;
-       class Naviframe;
-}
+#include "types.h"
 
-namespace callui {
+namespace callui { namespace utils {
 
        ucl::Result createCircleSurface(ucl::Naviframe &navi);
 
        Eext_Circle_Surface *getCircleSurface(const ucl::ElmWidget &widget);
 
-       void addRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data);
-
-       void delRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data);
+       ucl::ElmWidgetSRef createFakeAccessObject(ucl::ElmWidget &parent);
 
        Elm_Genlist_Item_Class createGenlistItemClass(const char *style,
                        Elm_Gen_Item_Text_Get_Cb txtCb = nullptr,
@@ -43,7 +39,27 @@ namespace callui {
                        Elm_Gen_Item_State_Get_Cb stateCb = nullptr,
                        Elm_Gen_Item_Del_Cb delCb = nullptr);
 
+       ucl::ElmWidgetSRef createAccessObject(ucl::ElmWidget &parent,
+                       ucl::Widget &ly);
+
+       ucl::ElmWidgetSRef createAccessObjectFromLyPart(ucl::ElmWidget &parent,
+                       ucl::Widget &ly,
+                       const ucl::EdjePart &lyPart);
+
+       void destroyAccessObject(ucl::ElmWidget &ao);
+
+}}
+
+namespace callui {
+
+       void addRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data);
+
+       void delRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data);
+
        ucl::LayoutTheme getImageTheme(const char *fileName);
+
+       Elm_Atspi_Relation_Type getFlowRelation(Elm_Atspi_Gesture_Info gestureInfo);
+
 }
 
 #endif // __CALLUI_VIEW_HELPERS_H__
index df1efbfec6e374335b5c658f35b11637fece3f0d..424ae3947fe79a15065fdb9fde9b43966f479875 100644 (file)
@@ -54,7 +54,8 @@ namespace callui {
        {
        }
 
-       AcceptDialog::Builder &AcceptDialog::Builder::setHandler(AcceptDialogHandler handler)
+       AcceptDialog::Builder &
+       AcceptDialog::Builder::setHandler(AcceptDialogHandler handler)
        {
                m_handler = handler;
                return *this;
@@ -110,11 +111,9 @@ namespace callui {
                if (!popupEo) {
                        LOG_RETURN(RES_FAIL, "elm_popup_add() failed!");
                }
-               evas_object_size_hint_weight_set(popupEo, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
-
                m_popup = makeShared<StyledWidget>(popupEo, true);
                m_popup->setStyle(style);
-
+               m_popup->setWeight(EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
                show(*m_popup);
 
                m_popup->addEventHandler(impl::POPUP_DISMISSED, WEAK_DELEGATE(
@@ -141,15 +140,17 @@ namespace callui {
                if (!glEo) {
                        LOG_RETURN(RES_FAIL, "elm_genlist_add() failed!");
                }
-               evas_object_size_hint_weight_set(glEo, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
-               evas_object_size_hint_align_set(glEo, EVAS_HINT_FILL, EVAS_HINT_FILL);
                elm_genlist_mode_set(glEo, ELM_LIST_COMPRESS);
                elm_genlist_homogeneous_set(glEo, EINA_TRUE);
 
                m_genlist = makeShared<StyledWidget>(glEo);
+               m_genlist->setWeight(EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+               m_genlist->setAlign(EVAS_HINT_FILL, EVAS_HINT_FILL);
 
-               Evas_Object *circleGlEo = eext_circle_object_genlist_add(glEo, getCircleSurface(*m_genlist));
-               eext_circle_object_genlist_scroller_policy_set(circleGlEo, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
+               Evas_Object *circleGlEo = eext_circle_object_genlist_add(glEo,
+                               utils::getCircleSurface(*m_genlist));
+               eext_circle_object_genlist_scroller_policy_set(circleGlEo,
+                               ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
                eext_rotary_object_event_activated_set(circleGlEo, EINA_TRUE);
 
                FAIL_RETURN(fillGenlist(), "fillGenlist() failed!");
@@ -161,7 +162,8 @@ namespace callui {
 
        Result AcceptDialog::addGenlistTitleItem()
        {
-               static Elm_Genlist_Item_Class titleItc = createGenlistItemClass("title",
+               static Elm_Genlist_Item_Class titleItc =
+                               utils::createGenlistItemClass("title",
                                [](void *data, Evas_Object *obj, const char *part) -> char * {
                                        return strdup(STR_ANSWER_CALL.translate());
                                });
@@ -180,7 +182,8 @@ namespace callui {
 
        Result AcceptDialog::addGenlistTextItem(AcceptDialogEvent event)
        {
-               static Elm_Genlist_Item_Class textItc = createGenlistItemClass("1text.1icon",
+               static Elm_Genlist_Item_Class textItc =
+                               utils::createGenlistItemClass("1text.1icon",
                                [](void *data, Evas_Object *obj, const char *part) -> char * {
                                        switch (impl::asEvent(data)) {
                                        case AcceptDialogEvent::HOLD_AND_ACCEPT:
@@ -206,8 +209,8 @@ namespace callui {
 
        Result AcceptDialog::addGenlistBottomItem()
        {
-               static Elm_Genlist_Item_Class paddingItc = createGenlistItemClass("1text.1icon");
-
+               static Elm_Genlist_Item_Class paddingItc =
+                               utils::createGenlistItemClass("1text.1icon");
                Elm_Object_Item *item = elm_genlist_item_append(*m_genlist, &paddingItc,
                                nullptr,
                                nullptr,
index 831637b9b2e6abfc292a5b847d7634bb9462b15d..19ebff12ea9a79557447e9aeb91e472e81037e90 100644 (file)
@@ -278,5 +278,14 @@ namespace callui {
                                AcceptButtonType::SIMPLE);
        }
 
+       // Screen Reader
+       ElmWidget *AcceptRejectPresenter::getAcceptAo()
+       {
+               return m_widget->getAcceptAo();
+       }
 
+       ElmWidget *AcceptRejectPresenter::getRejectAo()
+       {
+               return m_widget->getRejectAo();
+       }
 }
index 23787f01403d0643d977ebf914ad7bc58c693271..1774f0f3eed8e48e839f508689a9d73a1b0643a3 100644 (file)
@@ -51,6 +51,8 @@ namespace callui { namespace { namespace impl {
 
        constexpr EdjeSignal SIGNAL_TURN_ON {"turn.on"};
        constexpr EdjeSignal SIGNAL_TURN_OFF {"turn.off"};
+
+       constexpr SmartEvent EVENT_ACCESS_READ_START {"access,read,start"};
 }}}
 
 namespace callui {
@@ -112,7 +114,8 @@ namespace callui {
                        m_vcTimer(nullptr),
                        m_audioState(m_sm->getAudioState()),
                        m_mode(ComponentsMode::UNDEFINED),
-                       m_exitHandler(handler)
+                       m_exitHandler(handler),
+                       m_isVcShowOnRotaryEvent(false)
        {
        }
 
@@ -125,13 +128,17 @@ namespace callui {
        Result AccessoryPresenter::prepare(GuiPresenter &parent,
                        ElmWidget &parentWidget, const ICallManagerSRef &cm)
        {
-               FAIL_RETURN(GuiPresenter::prepare(parent), "Presenter::prepare() failed");
+               FAIL_RETURN(GuiPresenter::prepare(parent),
+                               "Presenter::prepare() failed");
 
-               FAIL_RETURN(createWidget(parentWidget), "createWidget() failed");
+               FAIL_RETURN(createWidget(parentWidget),
+                               "createWidget() failed");
 
-               FAIL_RETURN(createSlider(), "createSlider() failed");
+               FAIL_RETURN(createSlider(),
+                               "createSlider() failed");
 
-               FAIL_RETURN(createVolumeControl(), "createVolumeControl() failed");
+               FAIL_RETURN(createVolumeControl(),
+                               "createVolumeControl() failed");
 
                updateVolume(m_sm->getVolume());
 
@@ -139,7 +146,8 @@ namespace callui {
 
                updateMode(cm);
 
-               FAIL_RETURN(updateModeRelativeComponents(cm), "updateComponents() failed");
+               FAIL_RETURN(updateModeRelativeComponents(cm),
+                               "updateComponents() failed");
 
                return RES_OK;
        }
@@ -179,13 +187,10 @@ namespace callui {
 
        Result AccessoryPresenter::update(const ICallManagerSRef &cm)
        {
-               auto curMode = getCurrentMode(cm);
-               if (m_mode == curMode) {
-                       LOG_RETURN(RES_OK, "Mode is the same. No need to update");
-               }
-               m_mode = curMode;
+               updateMode(cm);
 
-               FAIL_RETURN(updateModeRelativeComponents(cm), "updateComponents() failed");
+               FAIL_RETURN(updateModeRelativeComponents(cm),
+                               "updateModeRelativeComponents() failed");
 
                return RES_OK;
        }
@@ -315,6 +320,8 @@ namespace callui {
                m_vc->resize(w, h);
                hide(*m_vc);
 
+               registerVolumeControlAo();
+
                return RES_OK;
        }
 
@@ -332,6 +339,10 @@ namespace callui {
                                                asWeak(*this)));
                show(*m_volumeBtn);
 
+               // Screen Reader
+               elm_atspi_accessible_translation_domain_set(*m_volumeBtn, PACKAGE);
+               elm_atspi_accessible_name_set(*m_volumeBtn, AO_STR_VOLUME);
+
                return RES_OK;
        }
 
@@ -349,6 +360,10 @@ namespace callui {
 
                show(*m_muteBtn);
 
+               // Screen Reader
+               elm_atspi_accessible_translation_domain_set(*m_muteBtn, PACKAGE);
+               elm_atspi_accessible_name_set(*m_muteBtn, AO_STR_MUTE);
+
                return RES_OK;
        }
 
@@ -366,9 +381,20 @@ namespace callui {
 
                show(*m_bluetoothBtn);
 
-               (m_audioState == AudioStateType::BT) ?
-                               m_bluetoothBtn->emit(impl::SIGNAL_TURN_ON) :
-                               m_bluetoothBtn->emit(impl::SIGNAL_TURN_OFF);
+               // Screen Reader
+               elm_atspi_accessible_translation_domain_set(*m_bluetoothBtn, PACKAGE);
+               if (m_audioState == AudioStateType::BT) {
+                       m_bluetoothBtn->emit(impl::SIGNAL_TURN_ON);
+                       // Screen Reader
+                       elm_atspi_accessible_name_set(*m_bluetoothBtn,
+                                       AO_STR_GEAR_SPK);
+               } else {
+                       m_bluetoothBtn->emit(impl::SIGNAL_TURN_OFF);
+                       // Screen Reader
+                       elm_atspi_accessible_name_set(*m_bluetoothBtn,
+                                       AO_STR_HEADSET);
+               }
+
                if (!m_sm->isBTSupported()) {
                        disable(*m_bluetoothBtn);
                }
@@ -389,6 +415,11 @@ namespace callui {
                                                asWeak(*this)));
                show(*m_addContactBtn);
 
+               // Screen Reader
+               elm_atspi_accessible_translation_domain_set(*m_addContactBtn, PACKAGE);
+               elm_atspi_accessible_name_set(*m_addContactBtn,
+                               AO_STR_ADD_TO_CONTACTS);
+
                return RES_OK;
        }
 
@@ -401,6 +432,8 @@ namespace callui {
 
                show(*m_vc);
                startVCTimer();
+
+               elm_atspi_component_highlight_grab(*m_vc);
        }
 
        void AccessoryPresenter::onMuteBtnClicked(Widget &widget, void *eventInfo)
@@ -531,7 +564,12 @@ namespace callui {
        {
                stopVCTimer();
 
-               m_vcTimer = ecore_timer_add(CALL_VC_TIMER_INTERVAL,
+               auto timerInterval = CALL_VC_TIMER_INTERVAL;
+               if (elm_atspi_bridge_utils_is_screen_reader_enabled()) {
+                       timerInterval = CALL_VC_SCREEN_READER_TIMER_INTERVAL;
+               }
+
+               m_vcTimer = ecore_timer_add(timerInterval,
                                CALLBACK_B(AccessoryPresenter::onVCTimerCb),
                                this);
        }
@@ -554,7 +592,8 @@ namespace callui {
        Eina_Bool AccessoryPresenter::onRotaryEvent(Eext_Rotary_Event_Info *info)
        {
                if (!isActive()) {
-                       LOG_RETURN_VALUE(RES_OK, EINA_TRUE, "Presenter is not active. Ignore");
+                       LOG_RETURN_VALUE(RES_OK, EINA_TRUE,
+                                       "Presenter is not active. Ignore");
                }
 
                if (m_vcTimer) {
@@ -562,6 +601,15 @@ namespace callui {
                } else {
                        show(*m_vc);
                        startVCTimer();
+                       m_isVcShowOnRotaryEvent = true;
+                       elm_atspi_component_highlight_grab(*m_vc);
+               }
+
+               if (m_isVcShowOnRotaryEvent) {
+                       m_isVcShowOnRotaryEvent = checkPossibilityToModifyVolume(
+                                       m_sm->getVolume(),
+                                       info->direction ==
+                                                       EEXT_ROTARY_DIRECTION_CLOCKWISE);
                }
 
                if (info->direction == EEXT_ROTARY_DIRECTION_CLOCKWISE) {
@@ -598,22 +646,32 @@ namespace callui {
                }
        }
 
+       bool AccessoryPresenter::checkPossibilityToModifyVolume(int volume, bool needIncrease)
+       {
+               if (needIncrease) {
+                       auto max = m_sm->getMaxVolume();
+                       return (max >= volume);
+               } else {
+                       return (volume - 1 >= VOLUME_LEVEL_MIN);
+               }
+               return false;
+       }
+
        void AccessoryPresenter::tryIncreaseVolume()
        {
-               auto max = m_sm->getMaxVolume();
                auto cur = m_sm->getVolume();
-
-               if (max != cur) {
-                       m_sm->setVolume(cur + 1);
+               if (checkPossibilityToModifyVolume(cur, true)) {
+                       FAIL_RETURN_VOID(m_sm->setVolume(cur + 1),
+                                       "setVolume() failed");
                }
        }
 
        void AccessoryPresenter::tryDecreaseVolume()
        {
                auto cur = m_sm->getVolume();
-
-               if (cur - 1 >= VOLUME_LEVEL_MIN) {
-                       m_sm->setVolume(cur - 1);
+               if (checkPossibilityToModifyVolume(cur, false)) {
+                       FAIL_RETURN_VOID(m_sm->setVolume(cur - 1),
+                                       "setVolume() failed");
                }
        }
 
@@ -635,9 +693,18 @@ namespace callui {
                        updateVolume(m_sm->getVolume());
 
                        if (m_bluetoothBtn) {
-                               (m_audioState == AudioStateType::BT) ?
-                                               m_bluetoothBtn->emit(impl::SIGNAL_TURN_ON) :
-                                               m_bluetoothBtn->emit(impl::SIGNAL_TURN_OFF);
+
+                               if (m_audioState == AudioStateType::BT) {
+                                       m_bluetoothBtn->emit(impl::SIGNAL_TURN_ON);
+                                       // Screen Reader
+                                       elm_atspi_accessible_name_set(*m_bluetoothBtn,
+                                                       AO_STR_GEAR_SPK);
+                               } else {
+                                       m_bluetoothBtn->emit(impl::SIGNAL_TURN_OFF);
+                                       // Screen Reader
+                                       elm_atspi_accessible_name_set(*m_bluetoothBtn,
+                                                       AO_STR_HEADSET);
+                               }
                        }
                }
        }
@@ -660,6 +727,15 @@ namespace callui {
                        m_vc->setIncreaseBtnEnable(true);
                        m_vc->setDecreaseBtnEnable(true);
                }
+
+               // Screen Reader
+               if (m_vc->isVisible()) {
+                       if (!m_isVcShowOnRotaryEvent) {
+                               elm_atspi_bridge_utils_say(std::to_string(cur).c_str(),
+                                               EINA_FALSE, nullptr, nullptr);
+                       }
+                       m_isVcShowOnRotaryEvent = false;
+               }
        }
 
        void AccessoryPresenter::onVolumeLevelChanged(int value)
@@ -684,5 +760,73 @@ namespace callui {
                isMuted ? m_muteBtn->emit(impl::SIGNAL_TURN_ON) :
                                m_muteBtn->emit(impl::SIGNAL_TURN_OFF);
        }
+
+       // Screen Reader
+       ElmWidget *AccessoryPresenter::getVolumBtn()
+       {
+               return m_volumeBtn.get();
+       }
+
+       ElmWidget *AccessoryPresenter::getBluetoothBtn()
+       {
+               return m_bluetoothBtn.get();
+       }
+
+       ElmWidget *AccessoryPresenter::getMuteBtn()
+       {
+               return m_muteBtn.get();
+       }
+
+       ElmWidget *AccessoryPresenter::getAddContactBtn()
+       {
+               return m_addContactBtn.get();
+       }
+
+       ElmWidget *AccessoryPresenter::getVolumeControlLy()
+       {
+               return m_vc.get();
+       }
+
+       ElmWidget *AccessoryPresenter::getVolumeControlDecreaseBtn()
+       {
+               return m_vc->getDecreaseBtn();
+       }
+
+       ElmWidget *AccessoryPresenter::getVolumeControlIncreaseBtn()
+       {
+               return m_vc->getIncreaseBtn();
+       }
+
+       ElmWidget *AccessoryPresenter::getVolumeControlValueTxtAo()
+       {
+               return m_vc->getValueTxtAo();
+       }
+
+       void AccessoryPresenter::registerVolumeControlAo()
+       {
+               auto decrBtn = m_vc->getDecreaseBtn();
+               if (decrBtn) {
+                       decrBtn->addEventHandler(impl::EVENT_ACCESS_READ_START,
+                                       WEAK_DELEGATE(AccessoryPresenter::
+                                                       onVolumeControlScreenReaderReadStart,
+                                                       asWeak(*this)));
+               }
+
+               auto incrBtn = m_vc->getIncreaseBtn();
+               if (incrBtn) {
+                       incrBtn->addEventHandler(impl::EVENT_ACCESS_READ_START,
+                                       WEAK_DELEGATE(AccessoryPresenter::
+                                                       onVolumeControlScreenReaderReadStart,
+                                                       asWeak(*this)));
+               }
+       }
+
+       void AccessoryPresenter::onVolumeControlScreenReaderReadStart(
+                       Widget &widget,
+                       void *eventInfo)
+       {
+               restartVCTimer();
+       }
+
 }
 
diff --git a/src/presenters/AtspiHighlightHelper.cpp b/src/presenters/AtspiHighlightHelper.cpp
new file mode 100644 (file)
index 0000000..b44c679
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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/AtspiHighlightHelper.h"
+
+#include "common.h"
+
+namespace callui { namespace { namespace impl {
+
+       constexpr EoDataKey ATSPI_HELPER_DATA {"callui,atspi,highlight,helper"};
+}}}
+
+namespace callui {
+
+       using ucl::AtspiGestureEventInfo;
+
+       using ucl::ATSPI_ON_GESTURE;
+
+       AtspiHighlightHelperSRef AtspiHighlightHelper::newInstance(
+                       GuiPresenter &parent, ElmWidget &rootWidget)
+       {
+               auto result = makeShared<AtspiHighlightHelper>();
+
+               FAIL_RETURN_VALUE(result->prepare(parent, rootWidget), {},
+                               "result->prepare() failed!");
+
+               return result;
+       }
+
+       AtspiHighlightHelper::AtspiHighlightHelper(IRefCountObj &rc) :
+               GuiPresenter(rc)
+       {
+       }
+
+       AtspiHighlightHelper::~AtspiHighlightHelper()
+       {
+       }
+
+       Result AtspiHighlightHelper::prepare(GuiPresenter &parent,
+                       ElmWidget &rootWidget)
+       {
+               FAIL_RETURN(GuiPresenter::prepare(parent),
+                               "GuiPresenter::prepare() failed!");
+
+               registerWidget(rootWidget);
+
+               return RES_OK;
+       }
+
+       void AtspiHighlightHelper::setRelationEventHandler(RelationEventHandler handler)
+       {
+               m_relationEventHandler = handler;
+       }
+
+       void AtspiHighlightHelper::setGestureEventHandler(GestureEventHandler handler)
+       {
+               m_gestureEventHandler = handler;
+       }
+
+       void AtspiHighlightHelper::registerWidget(ElmWidget &widget)
+       {
+               DLOG("this [%p] widget [%p]", this, widget.getEo());
+
+               widget.addEventHandler(ATSPI_ON_GESTURE, WEAK_DELEGATE(
+                               AtspiHighlightHelper::onAtspiGesture, asWeak(*this)));
+       }
+
+       void AtspiHighlightHelper::handleAtspiGesture(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       AtspiGestureEventInfo &e)
+       {
+               DLOG("this [%p] ao [%p]", this, ao);
+
+               if (e.stopPropagation) {
+                       DLOG("e.stopPropagation");
+                       return;
+               }
+
+               if (!isActive()) {
+                       DLOG("!isActive()");
+                       if (e.gestureInfo.type != ELM_ATSPI_GESTURE_ONE_FINGER_SINGLE_TAP) {
+                               e.preventDefault = true;
+                       }
+                       return;
+               }
+
+               e.stopPropagation = true;
+
+               if (m_gestureEventHandler) {
+                       DLOG("m_gestureEventHandler");
+                       if (m_gestureEventHandler(ao, e.gestureInfo.type)) {
+                               e.preventDefault = true;
+                               return;
+                       }
+               }
+
+               e.preventDefault = false;
+
+               if (!m_relationEventHandler) {
+                       DLOG("!m_relationEventHandler");
+                       return;
+               }
+
+               const Elm_Atspi_Relation_Type relation = getFlowRelation(e.gestureInfo);
+               if (relation == ELM_ATSPI_RELATION_NULL) {
+                       return;
+               }
+
+               const auto relationObj = m_relationEventHandler(ao, relation);
+               if (!relationObj) {
+                       return;
+               }
+
+               auto &win = getWindow();
+               auto atspiHelper = static_cast<Elm_Interface_Atspi_Accessible *>
+                               (win.getData(impl::ATSPI_HELPER_DATA));
+
+               if (!atspiHelper) {
+                       const auto obj = utils::createFakeAccessObject(win);
+                       if (!obj) {
+                               LOG_RETURN_VOID(RES_FAIL, "createFakeAccessObject() failed!");
+                       }
+                       obj->setIsOwner(false);
+                       atspiHelper = obj->getEo();
+                       win.setData(impl::ATSPI_HELPER_DATA, atspiHelper);
+               }
+
+               if (ao == win) {
+                       ao = atspiHelper;
+                       elm_atspi_component_highlight_grab(ao);
+               }
+
+               elm_atspi_accessible_relationships_clear(ao);
+               elm_atspi_accessible_relationship_append(ao, relation, relationObj);
+       }
+
+       void AtspiHighlightHelper::onAtspiGesture(
+                       Widget &widget, void *eventInfo)
+       {
+               handleAtspiGesture(widget,
+                               *static_cast<AtspiGestureEventInfo *>(eventInfo));
+       }
+
+       bool AtspiHighlightHelper::handleGesture(Elm_Interface_Atspi_Accessible *ao,
+                       const Elm_Atspi_Gesture_Info &info)
+       {
+               AtspiGestureEventInfo eventInfo {};
+               eventInfo.gestureInfo = info;
+
+               handleAtspiGesture(ao, eventInfo);
+
+               return eventInfo.preventDefault;
+       }
+}
index 9f787da9eea479fa8d9c0cf5a5c5f9c03800ee87..2a73963720bf2e6d7bc8dfeec070b6c93659d953 100644 (file)
@@ -78,6 +78,9 @@ namespace callui { namespace { namespace impl {
                "<align=center><color=#4dcfffff><font_size=24>%s"
                        "</color></font_size></align>"};
 
+       constexpr EdjePart PART_AO_MAIN_TXT {"ao_text_1line"};
+       constexpr EdjePart PART_AO_SUB_TXT {"ao_text_2line"};
+
        int getTextWidth(ElmWidget &parent, const char *fontStyle,
                        int fontSize, const std::string &text)
        {
@@ -356,6 +359,8 @@ namespace callui {
 
                m_widget->setContent(*m_label, impl::PART_SWL_2LINE);
 
+               elm_atspi_accessible_can_highlight_set(*m_label, EINA_FALSE);
+
                return RES_OK;
        }
 
@@ -452,6 +457,9 @@ namespace callui {
                        m_widget->emit(impl::SIGN_DEFAULT, impl::SRC_TXT_2LINE);
                }
 
+               FAIL_RETURN(setSubTxtAccessObject(subTxt),
+                               "setSubTxtAccessObject() failed!");
+
                return RES_OK;
        }
 
@@ -490,10 +498,13 @@ namespace callui {
        }
 
        void CallInfoPresenter::displayMainTxt(const ICallInfoSCRef &info,
-                       const std::string &text) const
+                       const std::string &text)
        {
                m_widget->setText(text.c_str(), impl::PART_TXT_MAIN);
 
+               FAIL_RETURN_VOID(setMainTxtAccessObject(text),
+                               "setMainTxtAccessObject() failed!");
+
                if (m_mode == CallMode::INCOMING) {
                        // Font size and color
                        if (m_callerId) {
@@ -646,4 +657,61 @@ namespace callui {
                return RES_OK;
        }
 
+       // Screen Reader
+       ElmWidget *CallInfoPresenter::getMainTxtAo()
+       {
+               return m_mainTxtAO.get();
+       }
+
+       ElmWidget *CallInfoPresenter::getSubTxtAo()
+       {
+               return m_subTxtAO.get();
+       }
+
+       ElmWidget *CallInfoPresenter::getStatusTxtAo()
+       {
+               return m_callStatus->getStatusTextAo();
+       }
+
+       Result CallInfoPresenter::setMainTxtAccessObject(const std::string &text)
+       {
+               if (!m_mainTxtAO) {
+                       m_mainTxtAO = utils::createAccessObjectFromLyPart(*m_widget,
+                                       *m_widget,
+                                       impl::PART_AO_MAIN_TXT);
+                       if (!m_mainTxtAO) {
+                               LOG_RETURN(RES_FAIL, "createAccessObjectFromLyPart() failed!");
+                       }
+
+               }
+               elm_atspi_accessible_reading_info_type_set(*m_mainTxtAO,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME);
+               elm_atspi_accessible_name_set(*m_mainTxtAO, text.c_str());
+
+               return RES_OK;
+       }
+
+       Result CallInfoPresenter::setSubTxtAccessObject(const std::string &text)
+       {
+               if (!text.empty()) {
+                       if (!m_subTxtAO) {
+                               m_subTxtAO = utils::createAccessObjectFromLyPart(*m_widget,
+                                               *m_widget,
+                                               impl::PART_AO_SUB_TXT);
+                               if (!m_subTxtAO) {
+                                       LOG_RETURN(RES_FAIL, "createAccessObjectFromLyPart() failed!");
+                               }
+                       }
+
+                       elm_atspi_accessible_reading_info_type_set(*m_subTxtAO,
+                                       ELM_ACCESSIBLE_READING_INFO_TYPE_NAME);
+                       elm_atspi_accessible_name_set(*m_subTxtAO, text.c_str());
+
+               } else if (m_subTxtAO) {
+                       utils::destroyAccessObject(*m_subTxtAO);
+                       m_subTxtAO.reset();
+               }
+
+               return RES_OK;
+       }
 }
index 61e6d6e01cce0760ce26fa8e1aef5c8a36bcc566..d613e9c202724bf0824b3dbb2071d902662d93b1 100644 (file)
@@ -35,6 +35,8 @@ namespace callui { namespace { namespace impl {
        constexpr EdjeSignal SIGN_DOT_LTR {"default:LTR"};
        constexpr EdjeSignal SIGN_RESET {"reset"};
 
+       constexpr EdjePart PART_AO_STATUS {"ao_text_info"};
+
        constexpr EdjeSignalSrc SIGN_SRC_DOT {"dot"};
 
 }}}
@@ -129,6 +131,7 @@ namespace callui {
 
                m_ly->emit(impl::SIGN_RESET, impl::SIGN_SRC_DOT);
                m_ly->setText("", impl::PART_TXT_TEXT_INFO);
+               createStatusTxtAo();
 
                switch (m_mode) {
                case CallMode::INCOMING:  return processIncomingMode();
@@ -190,7 +193,8 @@ namespace callui {
                        if (const auto info = m_info.lock()) {
                                m_duration = info->getDuration();
                        }
-                       setCallDuration(m_duration, *m_ly, impl::PART_TXT_TEXT_INFO);
+                       m_ly->setText(getCallDuration(m_duration),
+                                       impl::PART_TXT_TEXT_INFO);
 
                        if (m_timer) {
                                ecore_timer_del(m_timer);
@@ -211,7 +215,8 @@ namespace callui {
        Eina_Bool CallStatusPresenter::onBlinkingTimerCb()
        {
                if ((m_blinkCount % 2) == 0) {
-                       setCallDuration(m_duration, *m_ly, impl::PART_TXT_TEXT_INFO);
+                       m_ly->setText(getCallDuration(m_duration),
+                                       impl::PART_TXT_TEXT_INFO);
                } else if ((m_blinkCount % 2) == 1) {
                        m_ly->setText("", impl::PART_TXT_TEXT_INFO);
                }
@@ -246,4 +251,38 @@ namespace callui {
                return RES_OK;
        }
 
+       // Screen Reader
+       Result CallStatusPresenter::createStatusTxtAo()
+       {
+               if (!m_statusTxtAo) {
+                       m_statusTxtAo = utils::createAccessObjectFromLyPart(*m_ly,
+                                       *m_ly,
+                                       impl::PART_AO_STATUS);
+                       if (!m_statusTxtAo) {
+                               LOG_RETURN(RES_FAIL, "createAccessObjectFromLyPart() failed!");
+                       }
+
+                       elm_atspi_accessible_reading_info_type_set(*m_statusTxtAo,
+                                       ELM_ACCESSIBLE_READING_INFO_TYPE_NAME);
+                       elm_atspi_accessible_name_cb_set(*m_statusTxtAo,
+                                       [](void *data, Evas_Object *obj) -> char *
+                                       {
+                                               auto self = static_cast<CallStatusPresenter *>(data);
+                                               if (!self) {
+                                                       return nullptr;
+                                               }
+                                               auto txt = self->m_ly->
+                                                               getText(impl::PART_TXT_TEXT_INFO).getCStr();
+                                               return (txt) ? strdup(txt) : nullptr;
+                                       },
+                                       this);
+               }
+               return RES_OK;
+       }
+
+       ElmWidget *CallStatusPresenter::getStatusTextAo()
+       {
+               return m_statusTxtAo.get();
+       }
+
 }
index e77f8c411d7b419c31ebf9ce155fb4e201ddbc21..accfc96e63165cffac6dfa134a9d4e9da70081d7 100644 (file)
@@ -93,8 +93,10 @@ namespace callui {
                }
 
                m_win->getConformant().setContent(*m_navi);
+               elm_atspi_accessible_translation_domain_set(*m_win, PACKAGE);
+               elm_atspi_accessible_name_set(*m_win, AO_STR_CALL);
 
-               FAIL_RETURN(createCircleSurface(*m_navi),
+               FAIL_RETURN(utils::createCircleSurface(*m_navi),
                                "createCircleSurface() failed!");
 
                m_sysEventProvider.addEventHandler(
index 1a491b452675c7676e19fc47948653899863d594..689ddeb80450e63677971e446ba5239a3694edd4 100644 (file)
@@ -37,6 +37,8 @@
 #include "presenters/MoreOptionsPresenter.h"
 #include "presenters/DeviceStatePresenter.h"
 
+#include "presenters/AtspiHighlightHelper.h"
+
 #include "resources.h"
 #include "common.h"
 
@@ -61,8 +63,9 @@ namespace callui { namespace { namespace impl {
        constexpr EdjePart PART_SWL_OVERLAY {"swl.overlay"};
        constexpr EdjePart PART_SWL_MORE_OPTION {"swl.more_option"};
 
-       constexpr EdjePart PART_TXT_REJECT_MSG {"reject_msg_text"};
+       constexpr EdjePart PART_TXT_REJECT_MSG_CUE_AO {"ao_cue"};
 
+       constexpr EdjePart PART_TXT_REJECT_MSG {"reject_msg_text"};
        constexpr ElmStyle STYLE_BB_END_CALL {"callui/end_call"};
        constexpr ElmStyle STYLE_BB_RECALL {"callui/call_back"};
 }}}
@@ -204,8 +207,6 @@ namespace callui {
 
        Result MainPage::processIncomingCallMode()
        {
-               m_bottomBtn.reset();
-               m_moreOptionsPrs.reset();
                m_accessoryPrs.reset();
 
                auto call = m_cm->getIncomingCall();
@@ -223,19 +224,16 @@ namespace callui {
 
                if (!isUnknownCaller(*call->getInfo())
                                && (provider && provider->getMsgCount() > 0)) {
-                       m_rmLy = Layout::Builder().
-                                       setTheme(impl::LAYOUT_REJECT_MSG_WIDGET).
-                                       setIsOwner(true).
-                                       build(*m_widget);
-                       if (!m_rmLy) {
-                               LOG_RETURN(RES_FAIL, "Layout::build() failed!");
-                       }
-                       m_rmLy->setText(STR_DECLINE_MESSAGES, impl::PART_TXT_REJECT_MSG);
-
-                       m_widget->setContent(*m_rmLy, impl::PART_SWL_REJECT_MSG);
 
                        FAIL_RETURN(createRejectMsgPresenter(provider),
                                        "createRejectMsgPresenter() failed!");
+
+                       FAIL_RETURN(createRejectMsgCue(),
+                                       "craeteRejectMsgCue() failed!");
+
+                       if (createRejectMsgCueAo() != RES_OK) {
+                               ELOG("createRejectMsgCueAo() failed!");
+                       }
                }
 
                if (m_indicator) {
@@ -245,15 +243,24 @@ namespace callui {
                return RES_OK;
        }
 
-       Result MainPage::processEndCallMode()
+       Result MainPage::createRejectMsgCue()
        {
-               m_bottomBtn.reset();
-               m_moreOptionsPrs.reset();
+               m_rmLy = Layout::Builder().
+                               setTheme(impl::LAYOUT_REJECT_MSG_WIDGET).
+                               setIsOwner(true).
+                               build(*m_widget);
+               if (!m_rmLy) {
+                       LOG_RETURN(RES_FAIL, "Layout::build() failed!");
+               }
 
-               m_rmPrs.reset();
-               m_acceptRejectPrs.reset();
-               m_rmLy.reset();
+               m_rmLy->setText(STR_DECLINE_MESSAGES, impl::PART_TXT_REJECT_MSG);
+               m_widget->setContent(*m_rmLy, impl::PART_SWL_REJECT_MSG);
 
+               return RES_OK;
+       }
+
+       Result MainPage::processEndCallMode()
+       {
                if (m_indicator) {
                        m_indicator->udapteIncomingCallMode(false);
                }
@@ -261,6 +268,19 @@ namespace callui {
                FAIL_RETURN(createAccessoryPresenter(),
                                "createAccessoryPresenter() failed");
 
+
+               auto end = m_cm->getEndCall();
+               if (end) {
+                       auto info = end->getInfo();
+                       if (info && info->getConferenceMemberCount() == 1) {
+                               FAIL_RETURN(createBottomBtn(impl::STYLE_BB_RECALL, false),
+                                               "createBottomBtn() failed");
+                               hide(*m_bottomBtn);
+                       }
+               } else {
+                       ELOG("End call is NULL!");
+               }
+
                startEndCallTimer();
 
                return RES_OK;
@@ -271,6 +291,7 @@ namespace callui {
                m_rmPrs.reset();
                m_acceptRejectPrs.reset();
                m_rmLy.reset();
+               m_rmCueAo.reset();
 
                if (m_indicator) {
                        m_indicator->udapteIncomingCallMode(false);
@@ -363,7 +384,7 @@ namespace callui {
                }
        }
 
-       Result MainPage::createBottomBtn(const ElmStyle &style)
+       Result MainPage::createBottomBtn(const ElmStyle &style, bool setVisible)
        {
                m_bottomBtn = makeShared<StyledWidget>(
                                elm_button_add(*m_widget), true);
@@ -373,37 +394,42 @@ namespace callui {
                                WEAK_DELEGATE(MainPage::onBottomBtnClicked,
                                                asWeak(*this)));
 
-               m_widget->setContent(*m_bottomBtn, impl::PART_SWL_BOTTOM_BTN);
-               show(*m_bottomBtn);
+               elm_atspi_accessible_translation_domain_set(*m_bottomBtn, PACKAGE);
+               if (style == impl::STYLE_BB_RECALL) {
+                       elm_atspi_accessible_name_set(*m_bottomBtn, AO_STR_CALLBACK);
+               } else {
+                       elm_atspi_accessible_name_set(*m_bottomBtn, AO_STR_END_CALL);
+               }
+
+               if (setVisible) {
+                       m_widget->setContent(*m_bottomBtn, impl::PART_SWL_BOTTOM_BTN);
+                       show(*m_bottomBtn);
+               } else {
+                       hide(*m_bottomBtn);
+               }
 
                return RES_OK;
        }
 
        Eina_Bool MainPage::onEndCallTimerCb()
        {
-               auto end = m_cm->getEndCall();
-               if (!end) {
-                       m_ecTimer = nullptr;
-                       requestExit();
-                       LOG_RETURN_VALUE(RES_FAIL, ECORE_CALLBACK_CANCEL, "end is NULL");
-               }
-
-               auto info = end->getInfo();
-               if (!m_bottomBtn && !m_ecTimerBtnReq) {
-                       if (info && info->getConferenceMemberCount() == 1) {
-                               if (createBottomBtn(impl::STYLE_BB_RECALL) != RES_OK) {
-                                       m_ecTimer = nullptr;
-                                       requestExit();
-                                       LOG_RETURN_VALUE(RES_FAIL, ECORE_CALLBACK_CANCEL,
-                                                       "createBottomBtn() failed!");
-                               }
+               if (!m_ecTimerBtnReq) {
+                       if (!m_bottomBtn) {
+                               m_ecTimer = nullptr;
+                               requestExit();
+                               LOG_RETURN_VALUE(RES_FAIL, ECORE_CALLBACK_CANCEL, "bottom button is NULL!");
                        }
+                       m_widget->setContent(*m_bottomBtn, impl::PART_SWL_BOTTOM_BTN);
+                       show(*m_bottomBtn);
+
                        ecore_timer_interval_set(m_ecTimer, impl::CU_EXIT_APP_TIMEOUT);
                        m_ecTimerBtnReq = true;
+
                        return ECORE_CALLBACK_RENEW;
                } else {
                        m_ecTimer = nullptr;
                        requestExit();
+
                        return ECORE_CALLBACK_CANCEL;
                }
        }
@@ -424,6 +450,15 @@ namespace callui {
 
                FAIL_RETURN_VOID(showWindow(), "showWindow failed!");
 
+               m_acceptRejectPrs.reset();
+               m_rmPrs.reset();
+               m_moreOptionsPrs.reset();
+               m_callInfoPrs.reset();
+
+               m_rmLy.reset();
+               m_rmCueAo.reset();
+               m_bottomBtn.reset();
+
                switch (m_mode) {
                case CallMode::INCOMING:
                        FAIL_RETURN_VOID(processIncomingCallMode(),
@@ -443,7 +478,9 @@ namespace callui {
                                "createCallInfoPresenter() failed!");
 
                FAIL_RETURN_VOID(updateDeviceState(prevMode, m_mode),
-                               "createCallInfoPresenter() failed!");
+                               "updateDeviceState() failed!");
+
+               createAtspiHighlightHelper();
        }
 
        Result MainPage::updateDeviceState(CallMode prevMode, CallMode curMode)
@@ -464,13 +501,6 @@ namespace callui {
                return RES_OK;
        }
 
-       bool MainPage::detectMuteControlDisableState()
-       {
-               return (m_mode == CallMode::OUTGOING ||
-                               (m_mode == CallMode::DURING &&
-                                               (m_cm->getAvailableCalls() == CALL_FLAG_HELD)));
-       }
-
        Result MainPage::createWidget()
        {
                m_widget = Layout::Builder().
@@ -499,7 +529,8 @@ namespace callui {
                        LOG_RETURN(RES_FAIL, "Indicator::build() failed!");
                }
 
-               m_widget->setContent(m_indicator->getWidget(), impl::PART_SWL_INDICATOR);
+               m_widget->setContent(m_indicator->getWidget(),
+                               impl::PART_SWL_INDICATOR);
 
                return RES_OK;
        }
@@ -607,8 +638,13 @@ namespace callui {
 
        void MainPage::RejectMsgStateCb(RejectMsgState state)
        {
-               (state == RejectMsgState::HIDDEN) ?
-                               show(*m_rmLy) : hide(*m_rmLy);
+               if (state == RejectMsgState::HIDDEN) {
+                       show(*m_rmLy);
+                       show(*m_rmCueAo);
+               } else {
+                       hide(*m_rmLy);
+                       hide(*m_rmCueAo);
+               }
        }
 
        void MainPage::RejectMsgSelectCb(const IRejectMsgSRef &rm)
@@ -636,7 +672,8 @@ namespace callui {
                                        "RejectMessagePresenter::build() failed!");
                }
 
-               m_widget->setContent(m_rmPrs->getWidget(), impl::PART_SWL_OVERLAY);
+               m_widget->setContent(m_rmPrs->getWidget(),
+                               impl::PART_SWL_OVERLAY);
 
                return RES_OK;
        }
@@ -669,4 +706,458 @@ namespace callui {
                }
        }
 
+       // Screen Reader
+       Result MainPage::createRejectMsgCueAo()
+       {
+               m_rmCueAo = utils::createAccessObjectFromLyPart(*m_widget,
+                               *m_rmLy,
+                               impl::PART_TXT_REJECT_MSG_CUE_AO);
+               if (!m_rmCueAo) {
+                       LOG_RETURN(RES_FAIL, "createAccessObjectFromLyPart() failed!");
+               }
+               elm_atspi_accessible_translation_domain_set(*m_rmCueAo, PACKAGE);
+               elm_atspi_accessible_reading_info_type_set(*m_rmCueAo,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME |
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION);
+               elm_atspi_accessible_name_set(*m_rmCueAo, AO_STR_DECLINE_MESSAGES);
+               elm_atspi_accessible_description_set(*m_rmCueAo,
+                               AO_STR_SWIPE_UP_WITH_TWO_FINGERS_TO_SEND_A_DECLINE_MESSAGE);
+
+               elm_atspi_accessible_gesture_cb_set(*m_rmCueAo,
+                               [](void *data, Elm_Atspi_Gesture_Info gesture, Evas_Object *obj) {
+                               // TODO: ELM_ATSPI_GESTURE_TWO_FINGERS_HOVER must be replaced
+                                       if (gesture.type == ELM_ATSPI_GESTURE_TWO_FINGERS_HOVER) {
+                                               auto page = (MainPage *)data;
+                                               page->m_rmPrs->showPanel();
+                                               return EINA_TRUE;
+                                       }
+
+                                       return EINA_FALSE;
+                               }, this);
+
+               return RES_OK;
+       }
+
+       Result MainPage::createAtspiHighlightHelper()
+       {
+               m_atspiHelper = AtspiHighlightHelper::newInstance(*this, getWindow());
+               if (!m_atspiHelper) {
+                       LOG_RETURN(RES_FAIL,
+                                       "AtspiHighlightHelper::newInstance() failed!");
+               }
+
+               switch (m_mode) {
+               case CallMode::INCOMING:
+                       registerIncomingCallModeAo();
+                       break;
+               case CallMode::OUTGOING:
+               case CallMode::DURING:
+                       registerActiveCallModeAo();
+                       break;
+               case CallMode::END:
+                       registerEndCallModeAo();
+               default:
+                       break;
+               }
+               return RES_OK;
+       }
+
+       void MainPage::registerIncomingCallModeAo()
+       {
+               DLOG("ENTER");
+               m_atspiHelper->setRelationEventHandler(WEAK_DELEGATE(
+                               MainPage::onIncomingCallModeAtspiHighlight, asWeak(*this)));
+
+               m_atspiHelper->setGestureEventHandler(WEAK_DELEGATE(
+                               MainPage::onIncomingModeAtspiGesture, asWeak(*this)));
+
+               auto acceptAO = m_acceptRejectPrs->getAcceptAo();
+               if (!acceptAO) {
+                       DLOG("acceptAO is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*acceptAO);
+               }
+
+               auto rejectAo = m_acceptRejectPrs->getRejectAo();
+               if (!rejectAo) {
+                       DLOG("rejectAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*rejectAo);
+               }
+
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               if (!statusTxtAo) {
+                       DLOG("statusTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*statusTxtAo);
+               }
+
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               if (!mainTxtAo) {
+                       DLOG("mainTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*mainTxtAo);
+               }
+
+               auto subTxtAo = m_callInfoPrs->getSubTxtAo();
+               if (!subTxtAo) {
+                       DLOG("subTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*subTxtAo);
+               }
+
+               if (!m_rmCueAo) {
+                       DLOG("m_rmCueAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*m_rmCueAo);
+               }
+
+               DLOG("EXIT");
+       }
+
+       bool MainPage::onIncomingModeAtspiGesture(Elm_Interface_Atspi_Accessible *widget,
+                       Elm_Atspi_Gesture_Type gestureType)
+       {
+               DLOG(" GESTURE TYPE [%d]", gestureType);
+
+               if (m_rmCueAo && widget == *m_rmCueAo) {
+                       if (gestureType == ELM_ATSPI_GESTURE_TWO_FINGERS_HOVER) {
+                               m_rmPrs->showPanel();
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       Elm_Interface_Atspi_Accessible *MainPage::onIncomingCallModeAtspiHighlight(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       Elm_Atspi_Relation_Type flowRelation)
+       {
+               DLOG("FlowRelation [%s]",
+                               flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM ?
+                                               "FROM" : "TO");
+
+               auto acceptAo = m_acceptRejectPrs->getAcceptAo();
+               auto rejectAo = m_acceptRejectPrs->getRejectAo();
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               auto subTxtAo = m_callInfoPrs->getSubTxtAo();
+
+               if (ao == *acceptAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *statusTxtAo;
+                       }
+               } else if (ao == *statusTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *mainTxtAo;
+                       } else if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *acceptAo;
+                       }
+               } else if (ao == *mainTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return (subTxtAo) ? *subTxtAo : *rejectAo;
+                       } else if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *statusTxtAo;
+                       }
+               } else if (subTxtAo && ao == *subTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *rejectAo;
+                       } else if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *mainTxtAo;
+                       }
+               } else if (ao == *rejectAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return m_rmCueAo ? *m_rmCueAo : ao;
+                       } else if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return (subTxtAo) ? *subTxtAo : *mainTxtAo;
+                       }
+               } else if (m_rmCueAo && ao == *m_rmCueAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *rejectAo;
+                       }
+               } else if (ao == getWindow()) {
+                       return *acceptAo;
+               } else {
+                       LOG_RETURN_VALUE(RES_FAIL, nullptr, "Unknown object!");
+               }
+
+               return ao;
+       }
+
+       void MainPage::registerActiveCallModeAo()
+       {
+               DLOG("ENTER");
+               m_atspiHelper->setRelationEventHandler(WEAK_DELEGATE(
+                               MainPage::onActiveCallModeAtspiHighlight, asWeak(*this)));
+
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               if (!statusTxtAo) {
+                       DLOG("statusTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*statusTxtAo);
+               }
+
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               if (!mainTxtAo) {
+                       DLOG("mainTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*mainTxtAo);
+               }
+
+               auto subTxtAo = m_callInfoPrs->getSubTxtAo();
+               if (!subTxtAo) {
+                       DLOG("subTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*subTxtAo);
+               }
+
+               auto volumeBtnAo = m_accessoryPrs->getVolumBtn();
+               if (!volumeBtnAo) {
+                       DLOG("volumeBtnAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*volumeBtnAo);
+               }
+
+               auto bluetoothBtnAo = m_accessoryPrs->getBluetoothBtn();
+               if (!bluetoothBtnAo) {
+                       DLOG("bluetoothBtnAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*bluetoothBtnAo);
+               }
+
+               auto muteBtnAo = m_accessoryPrs->getMuteBtn();
+               if (!muteBtnAo) {
+                       DLOG("muteBtnAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*muteBtnAo);
+               }
+
+               auto moreOptCueAo = m_moreOptionsPrs->getCueAo();
+               if (!moreOptCueAo) {
+                       DLOG("moreCueAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*moreOptCueAo);
+               }
+
+               if (!m_bottomBtn) {
+                       DLOG("m_bottomBtn is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*m_bottomBtn);
+               }
+
+               auto vcLayout = m_accessoryPrs->getVolumeControlLy();
+               if (!vcLayout) {
+                       DLOG("vcLayout is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*vcLayout);
+               }
+
+               auto vcDecrVolumeBtn = m_accessoryPrs->getVolumeControlDecreaseBtn();
+               if (!vcDecrVolumeBtn) {
+                       DLOG("vcDecrVolumeBtn is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*vcDecrVolumeBtn);
+               }
+
+               auto vcIncrVolumeBtn = m_accessoryPrs->getVolumeControlIncreaseBtn();
+               if (!vcIncrVolumeBtn) {
+                       DLOG("vcIncrVolumeBtn is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*vcIncrVolumeBtn);
+               }
+
+               auto vcVolumeValueAo = m_accessoryPrs->getVolumeControlValueTxtAo();
+               if (!vcVolumeValueAo) {
+                       DLOG("vcVolumeValueAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*vcVolumeValueAo);
+               }
+
+               DLOG("EXIT");
+       }
+
+       Elm_Interface_Atspi_Accessible *MainPage::onActiveCallModeAtspiHighlight(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       Elm_Atspi_Relation_Type flowRelation)
+       {
+               DLOG("FlowRelation [%s]",
+                               flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM ?
+                                               "FROM" : "TO");
+
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               auto subTxtAo = m_callInfoPrs->getSubTxtAo();
+               auto volumeBtnAo = m_accessoryPrs->getVolumBtn();
+               auto bluetoothBtnAo = m_accessoryPrs->getBluetoothBtn();
+               auto muteBtnAo = m_accessoryPrs->getMuteBtn();
+               auto moreOptCueAo = m_moreOptionsPrs->getCueAo();
+               auto vcLayout = m_accessoryPrs->getVolumeControlLy();
+               auto vcDecrVolumeBtn = m_accessoryPrs->getVolumeControlDecreaseBtn();
+               auto vcIncrVolumeBtn = m_accessoryPrs->getVolumeControlIncreaseBtn();
+               auto vcVolumeValueAo = m_accessoryPrs->getVolumeControlValueTxtAo();
+
+               if (ao == *statusTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *mainTxtAo;
+                       }
+               } else if (ao == *mainTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               if (subTxtAo) {
+                                       return *subTxtAo;
+                               } else {
+                                       return *volumeBtnAo;
+                               }
+                       } else {
+                               return *statusTxtAo;
+                       }
+               } else if (subTxtAo && ao == *subTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *volumeBtnAo;
+                       } else {
+                               return *mainTxtAo;
+                       }
+               } else if (ao == *volumeBtnAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *bluetoothBtnAo;
+                       } else {
+                               if (subTxtAo) {
+                                       return *subTxtAo;
+                               } else {
+                                       return *mainTxtAo;
+                               }
+                       }
+               } else if (ao == *bluetoothBtnAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *muteBtnAo;
+                       } else {
+                               return *volumeBtnAo;
+                       }
+               } else if (ao == *muteBtnAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *moreOptCueAo;
+                       } else {
+                               return *bluetoothBtnAo;
+                       }
+               } else if (ao == *moreOptCueAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                                       return *m_bottomBtn;
+                       } else {
+                               return *muteBtnAo;
+                       }
+               } else if (ao == *m_bottomBtn) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *moreOptCueAo;
+                       }
+               } else if (ao == *vcLayout) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *vcDecrVolumeBtn;
+                       }
+               } else if (ao == *vcDecrVolumeBtn) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *vcVolumeValueAo;
+                       } else {
+                               return *vcLayout;
+                       }
+               } else if (ao == *vcVolumeValueAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *vcIncrVolumeBtn;
+                       } else {
+                               return *vcDecrVolumeBtn;
+                       }
+               } else if (ao == *vcIncrVolumeBtn) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *vcVolumeValueAo;
+                       }
+               } else if (ao == getWindow()) {
+                       return *statusTxtAo;
+               } else {
+                       LOG_RETURN_VALUE(RES_FAIL, nullptr, "Unknown object!");
+               }
+
+               return ao;
+       }
+
+       void MainPage::registerEndCallModeAo()
+       {
+               m_atspiHelper->setRelationEventHandler(WEAK_DELEGATE(
+                               MainPage::onEndCallModeAtspiHighlight, asWeak(*this)));
+
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               if (!statusTxtAo) {
+                       DLOG("statusTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*statusTxtAo);
+               }
+
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               if (!mainTxtAo) {
+                       DLOG("mainTxtAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*mainTxtAo);
+               }
+
+               auto addContactsBtnAo = m_accessoryPrs->getAddContactBtn();
+               if (!addContactsBtnAo) {
+                       DLOG("addContactsBtnAo is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*addContactsBtnAo);
+               }
+
+               if (!m_bottomBtn) {
+                       DLOG("m_bottomBtn is NULL");
+               } else {
+                       m_atspiHelper->registerWidget(*m_bottomBtn);
+               }
+
+               DLOG("EXIT");
+       }
+
+       Elm_Interface_Atspi_Accessible *MainPage::onEndCallModeAtspiHighlight(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       Elm_Atspi_Relation_Type flowRelation)
+       {
+               DLOG("FlowRelation [%s]",
+                               flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM ?
+                                               "FROM" : "TO");
+
+               auto statusTxtAo = m_callInfoPrs->getStatusTxtAo();
+               auto mainTxtAo = m_callInfoPrs->getMainTxtAo();
+               auto addContactsBtnAo = m_accessoryPrs->getAddContactBtn();
+
+               if (ao == *statusTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *mainTxtAo;
+                       }
+               } else if (ao == *mainTxtAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               if (addContactsBtnAo) {
+                                       return *addContactsBtnAo;
+                               } else {
+                                       return *m_bottomBtn;
+                               }
+                       } else {
+                               return *statusTxtAo;
+                       }
+               } else if (addContactsBtnAo && ao == *addContactsBtnAo) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *m_bottomBtn;
+                       } else {
+                               return *mainTxtAo;
+                       }
+               } else if (ao == *m_bottomBtn) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               if (addContactsBtnAo) {
+                                       return *addContactsBtnAo;
+                               } else {
+                                       return *mainTxtAo;
+                               }
+                       }
+               } else if (ao == getWindow()) {
+                       return *statusTxtAo;
+               } else {
+                       LOG_RETURN_VALUE(RES_FAIL, nullptr, "Unknown object!");
+               }
+
+               return ao;
+       }
 }
index 6a98eac8925b17be25f12d07e4daf8264bc6bd5a..13f60490ba5b732bef21824ab4be2b92e86d0f85 100644 (file)
@@ -28,6 +28,7 @@
 #include "model/ISoundManager.h"
 
 #include "presenters/KeypadPage.h"
+#include "presenters/AtspiHighlightHelper.h"
 
 #include "resources.h"
 #include "common.h"
@@ -54,6 +55,10 @@ namespace callui { namespace { namespace impl {
        constexpr EdjePart PART_SWL_SLOT2 {"swl.slot.2"};
        constexpr EdjePart PART_TXT_STATUS {"txt.status"};
 
+       constexpr EdjePart PART_AO_TXT_STATUS {"ao_txt.status"};
+
+       constexpr EdjePart PART_ACCESS {"access"};
+
        constexpr EdjeSignal SIGNAL_ODD {"odd"};
        constexpr EdjeSignal SIGNAL_EVEN {"even"};
 
@@ -156,9 +161,11 @@ namespace callui {
 
                FAIL_RETURN(createPanelLayout(), "createPanelContent() failed!");
 
-               FAIL_RETURN(createButtons(), "createButtons() failed!");
+               FAIL_RETURN(createAccessObjects(), "createStaticAo() failed!");
 
-               update();
+               updateComponents();
+
+               FAIL_RETURN(createAtspiHighlightHelper(), "createAtspiHighlightHelper() failed!");
 
                deactivateBy(m_widget.get());
 
@@ -181,6 +188,20 @@ namespace callui {
                return RES_OK;
        }
 
+       void MoreOptionsPresenter::onCueClicked(Evas_Object *obj,
+                       const char *emission,
+                       const char *source)
+       {
+               show(*m_fakeAo);
+               m_panel->setContent(*m_panelLy);
+               show(*m_panelLy);
+
+               createStatusTxtAo();
+               if (m_statusTxtAo) {
+                       m_atspiHelper->registerWidget(*m_statusTxtAo);
+               }
+       }
+
        Result MoreOptionsPresenter::createPanel()
        {
                auto *eo = elm_panel_add(*m_widget);
@@ -201,6 +222,11 @@ namespace callui {
                elm_layout_signal_callback_add(*m_panel, "elm,state,inactive,finished",
                                "elm", CALLBACK_A(MoreOptionsPresenter::onPanelInactivate), this);
 
+               setDeactivatorSink(m_panel);
+
+               elm_layout_signal_callback_add(*m_panel, "cue,clicked",
+                               "elm", CALLBACK_A(MoreOptionsPresenter::onCueClicked), this);
+
                return RES_OK;
        }
 
@@ -226,12 +252,19 @@ namespace callui {
                auto active = m_cm->getActiveCall();
                auto held = m_cm->getHeldCall();
 
+               FAIL_RETURN_VOID(createKeypadButton(),
+                               "createKeypadButton() failed");
+
                if (held) {
                        m_panelLy->emit(impl::SIGNAL_EVEN, impl::SIGNAL_SRC_MORE_OPTION);
 
                        if (active) {
+                               FAIL_RETURN_VOID(createSwapButton(),
+                                               "createSwapButton() failed");
                                setPanelContent(*m_btnSwap, impl::PART_SWL_SLOT1);
                        } else {
+                               FAIL_RETURN_VOID(createUnholdButton(),
+                                               "createUnholdButton() failed");
                                setPanelContent(*m_btnUnhold, impl::PART_SWL_SLOT1);
                        }
                        setPanelContent(*m_btnKeypad, impl::PART_SWL_SLOT2);
@@ -242,9 +275,8 @@ namespace callui {
                }
        }
 
-       Result MoreOptionsPresenter::createButtons()
+       Result MoreOptionsPresenter::createSwapButton()
        {
-               // Swap
                m_btnSwap = createButton(impl::STYLE_BTN_SWAP, STR_MORE_SWAP,
                                WEAK_DELEGATE(MoreOptionsPresenter::onSwapBtnClick,
                                                asWeak(*this)));
@@ -252,7 +284,11 @@ namespace callui {
                        LOG_RETURN(RES_FAIL, "Create Swap button failed!");
                }
 
-               // Unhold
+               return RES_OK;
+       }
+
+       Result MoreOptionsPresenter::createUnholdButton()
+       {
                m_btnUnhold = createButton(impl::STYLE_BTN_UNHOLD, STR_MORE_UNHOLD,
                                WEAK_DELEGATE(MoreOptionsPresenter::onUnholdBtnClick,
                                                asWeak(*this)));
@@ -260,7 +296,11 @@ namespace callui {
                        LOG_RETURN(RES_FAIL, "Create Unhold button failed!");
                }
 
-               // Keypad
+               return RES_OK;
+       }
+
+       Result MoreOptionsPresenter::createKeypadButton()
+       {
                m_btnKeypad = createButton(impl::STYLE_BTN_KEYPAD, STR_MORE_KEYPAD,
                                WEAK_DELEGATE(MoreOptionsPresenter::onKeypadBtnClick,
                                                asWeak(*this)));
@@ -335,6 +375,11 @@ namespace callui {
        }
 
        void MoreOptionsPresenter::update()
+       {
+               updateComponents();
+       }
+
+       void MoreOptionsPresenter::updateComponents()
        {
                updateSlots();
                updateStatusText();
@@ -350,6 +395,8 @@ namespace callui {
                const auto keepAliver = asShared(*this);
                sendDeactivate(*m_widget);
                activateBy(m_widget.get());
+
+               elm_atspi_component_highlight_grab(*m_fakeAo);
        }
 
        void MoreOptionsPresenter::onPanelInactivate(Evas_Object *obj,
@@ -362,6 +409,13 @@ namespace callui {
                const auto keepAliver = asShared(*this);
                deactivateBy(m_widget.get());
                sendActivate(*m_widget);
+
+               m_panel->unsetContent();
+               hide(*m_panelLy);
+               m_statusTxtAo.reset();
+
+               elm_atspi_accessible_can_highlight_set(*m_cueAo, EINA_TRUE);
+               hide(*m_fakeAo);
        }
 
        void MoreOptionsPresenter::onBackKey(Evas_Object *obj, void *eventInfo)
@@ -391,7 +445,8 @@ namespace callui {
                        }
                        m_info = info;
                        m_duration = info->getDuration();
-                       setCallDuration(m_duration, *m_panelLy, impl::PART_TXT_STATUS);
+                       auto temp = getCallDuration(m_duration);
+                       m_panelLy->setText(temp, impl::PART_TXT_STATUS);
 
                        FAIL_RETURN_VOID(startCallDurationTimer(), "startTimer() failed!");
 
@@ -454,4 +509,198 @@ namespace callui {
                m_panelLy->setContent(widget, part);
                show(widget);
        }
+
+       // Screen Reader
+       Result MoreOptionsPresenter::createAtspiHighlightHelper()
+       {
+               DLOG("ENTER");
+               m_atspiHelper = AtspiHighlightHelper::newInstance(*this, getWindow());
+               if (!m_atspiHelper) {
+                       LOG_RETURN(RES_FAIL,
+                                       "AtspiHighlightHelper::newInstance() failed!");
+               }
+
+               m_atspiHelper->setRelationEventHandler(WEAK_DELEGATE(
+                               MoreOptionsPresenter::onAtspiHighlight, asWeak(*this)));
+
+               if (m_panelLy) {
+                       m_atspiHelper->registerWidget(*m_fakeAo);
+               }
+
+               if (m_panelLy) {
+                       m_atspiHelper->registerWidget(*m_panelLy);
+               }
+
+               if (m_btnSwap) {
+                       m_atspiHelper->registerWidget(*m_btnSwap);
+               }
+
+               if (m_btnUnhold) {
+                       m_atspiHelper->registerWidget(*m_btnUnhold);
+               }
+
+               if (m_btnKeypad) {
+                       m_atspiHelper->registerWidget(*m_btnKeypad);
+               }
+
+               if (m_statusTxtAo) {
+                       m_atspiHelper->registerWidget(*m_statusTxtAo);
+               }
+               DLOG("EXIT");
+               return RES_OK;
+       }
+
+       Elm_Interface_Atspi_Accessible *MoreOptionsPresenter::onAtspiHighlight(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       Elm_Atspi_Relation_Type flowRelation)
+       {
+               DLOG("FlowRelation [%s]",
+                               flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM ?
+                                               "FROM" :
+                                               "TO");
+
+               if (m_btnSwap && ao == *m_btnSwap) {
+                       DLOG("Swap button");
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *m_btnKeypad;
+                       }
+               } else if (m_btnUnhold && ao == *m_btnUnhold) {
+                       DLOG("Unhold button");
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *m_btnKeypad;
+                       }
+               } else if (ao == *m_btnKeypad) {
+                       DLOG("Keypad button");
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return *m_statusTxtAo;
+                       } else {
+                               if (m_btnSwap) {
+                                       return *m_btnSwap;
+                               } else if (m_btnUnhold) {
+                                       return *m_btnUnhold;
+                               }
+                       }
+               } else if (ao == *m_statusTxtAo) {
+                       DLOG("Status text");
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return *m_btnKeypad;
+                       }
+               } else if (ao == getWindow() || ao == *m_fakeAo) {
+                       DLOG("window or panelLy");
+                       if (m_btnSwap) {
+                               return *m_btnSwap;
+                       } else if (m_btnUnhold) {
+                               return *m_btnUnhold;
+                       } else {
+                               return *m_btnKeypad;
+                       }
+               } else {
+                       LOG_RETURN_VALUE(RES_FAIL, nullptr, "Unknown object!");
+               }
+
+               return ao;
+       }
+
+       Result MoreOptionsPresenter::createAccessObjects()
+       {
+               FAIL_RETURN(createCueAo(), "createCueAo() failed");
+               FAIL_RETURN(createStatusTxtAo(), "createStatusTxtAo() failed");
+               FAIL_RETURN(createFakeAo(), "createFakeAo() failed");
+
+               return RES_OK;
+       }
+
+       Result MoreOptionsPresenter::createCueAo()
+       {
+               m_cueAo = utils::createAccessObjectFromLyPart(*m_widget,
+                               *m_panel,
+                               impl::PART_ACCESS);
+               if (!m_cueAo) {
+                       LOG_RETURN(RES_FAIL,
+                                       "createAccessObjectFromLyPart() failed!");
+               }
+               elm_access_action_cb_set(*m_cueAo,
+                               ELM_ACCESS_ACTION_ACTIVATE,
+                               CALLBACK_A(MoreOptionsPresenter::onCueAoActionCb),
+                               this);
+
+               elm_atspi_accessible_translation_domain_set(*m_cueAo, PACKAGE);
+               elm_atspi_accessible_reading_info_type_set(*m_cueAo,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME |
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_ROLE);
+               elm_atspi_accessible_name_set(*m_cueAo, AO_STR_MORE_OPTIONS);
+               elm_atspi_accessible_role_set(*m_cueAo, ELM_ATSPI_ROLE_PUSH_BUTTON);
+
+               return RES_OK;
+       }
+
+       ElmWidget *MoreOptionsPresenter::getCueAo()
+       {
+               return m_cueAo.get();
+       }
+
+       Result MoreOptionsPresenter::createFakeAo()
+       {
+               m_fakeAo = utils::createFakeAccessObject(*m_panel);
+               if (!m_fakeAo) {
+                       LOG_RETURN(RES_FAIL, "createFakeAccessObject() failed!");
+               }
+
+               return RES_OK;
+       }
+
+       Result MoreOptionsPresenter::createStatusTxtAo()
+       {
+               m_statusTxtAo = utils::createAccessObjectFromLyPart(*m_widget,
+                               *m_panelLy,
+                               impl::PART_AO_TXT_STATUS);
+               if (!m_statusTxtAo) {
+                       LOG_RETURN(RES_FAIL,
+                                       "createAccessObjectFromLyPart() failed!");
+               }
+
+               elm_atspi_accessible_reading_info_type_set(*m_statusTxtAo,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME);
+               elm_atspi_accessible_name_cb_set(*m_statusTxtAo,
+                               [](void *data, Evas_Object *obj) -> char *
+                               {
+                                       auto self = static_cast<MoreOptionsPresenter *>(data);
+                                       if (!self) {
+                                               return nullptr;
+                                       }
+                                       auto txt = self->m_panelLy->
+                                                       getText(impl::PART_TXT_STATUS).getCStr();
+                                       return (txt) ? strdup(txt) : nullptr;
+                               },
+                               this);
+
+               return RES_OK;
+       }
+
+       Eina_Bool MoreOptionsPresenter::onCueAoActionCb(Evas_Object *obj,
+                               Elm_Access_Action_Info *actionInfo)
+       {
+               switch (actionInfo->action_type) {
+               case ELM_ACCESS_ACTION_ACTIVATE:
+
+                       show(*m_fakeAo);
+                       m_panel->setContent(*m_panelLy);
+                       show(*m_panelLy);
+                       createStatusTxtAo();
+                       if (m_statusTxtAo) {
+                               m_atspiHelper->registerWidget(*m_statusTxtAo);
+                       }
+
+                       elm_panel_toggle(*m_panel);
+                       elm_atspi_component_highlight_clear(*m_cueAo);
+                       elm_atspi_accessible_can_highlight_set(*m_cueAo, EINA_FALSE);
+
+                       return EINA_TRUE;
+                       break;
+               default:
+                       break;
+               }
+
+               return EINA_FALSE;
+       }
 }
index 77743218dd15f02ceec3eca717b72fda7c3668ca..f5e7c03c5eba1d64f045b0a00b86609ff8eb8456 100644 (file)
@@ -19,6 +19,8 @@
 #include "model/IRejectMsgProvider.h"
 #include "model/IRejectMsg.h"
 
+#include "presenters/AtspiHighlightHelper.h"
+
 #include "resources.h"
 #include "common.h"
 
@@ -174,8 +176,8 @@ namespace callui {
                FAIL_RETURN(createPanelLy(),
                                "createPanelLy() failed!");
 
-               FAIL_RETURN(createGenlist(),
-                               "createGenlist() failed!");
+               FAIL_RETURN(createAtspiHighlightHelper(),
+                               "createScreenReaderRoute() failed!");
 
                deactivateBy(m_widget.get());
 
@@ -212,30 +214,43 @@ namespace callui {
        {
                Elm_Panel_Scroll_Info *ev = static_cast<Elm_Panel_Scroll_Info *>(eventInfo);
                DLOG("pos x[%f] y[%f]", ev->rel_x, ev->rel_y);
+               auto prevState = m_state;
 
                if (ev->rel_y == 1.0) {
                        m_state = RejectMsgState::SHOWN;
                        // Prevent panel scrolling
                        elm_object_scroll_freeze_push(m_panel->getEo());
-                       sendDeactivate(*m_widget);
-                       activateBy(m_widget.get());
                } else if (ev->rel_y == 0.0) {
                        m_state = RejectMsgState::HIDDEN;
-                       // Scroll genlist to top
-                       elm_scroller_region_show(m_genlist->getEo(), 0, 0, 0, 0);
+                       m_genlist.reset();
                } else {
+                       if (!m_genlist) {
+                               FAIL_RETURN_VOID(createGenlist(),
+                                               "createGenlist() failed!");
+                       }
+
                        m_state = RejectMsgState::IN_TRANSITION;
                        const auto alphaValue =
                                        static_cast<int>(impl::ALPHA_CHANNEL_MAX * ev->rel_y);
                        m_panelBg->setColor(0, alphaValue);
-                       if (isActive()) {
+               }
+
+               if (prevState != m_state) {
+                       if (m_state == RejectMsgState::SHOWN) {
+                               activateBy(m_widget.get());
+                       } else {
                                deactivateBy(m_widget.get());
+                       }
+
+                       if (m_state == RejectMsgState::HIDDEN) {
                                sendActivate(*m_widget);
+                       } else {
+                               sendDeactivate(*m_widget);
                        }
-               }
 
-               if (m_stateHandler) {
-                       m_stateHandler(m_state);
+                       if (m_stateHandler) {
+                               m_stateHandler(m_state);
+                       }
                }
        }
 
@@ -257,6 +272,8 @@ namespace callui {
 
                m_widget->setContent(*m_panel, impl::PART_SWL_RIGHT);
 
+               setDeactivatorSink(m_panel);
+
                return RES_OK;
        }
 
@@ -297,7 +314,8 @@ namespace callui {
                        LOG_RETURN(RES_FAIL, "Layout::build failed!");
                }
                // Circular surface
-               Eext_Circle_Surface *const circleSurf = eext_circle_surface_layout_add(*circlLy);
+               Eext_Circle_Surface *const circleSurf =
+                               eext_circle_surface_layout_add(*circlLy);
                if (!circleSurf) {
                        LOG_RETURN(RES_FAIL, "eext_circle_surface_layout_add() failed!");
                }
@@ -306,17 +324,21 @@ namespace callui {
                if (!glEo) {
                        LOG_RETURN(RES_FAIL, "elm_genlist_add() failed!");
                }
-               evas_object_size_hint_weight_set(glEo, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
-               evas_object_size_hint_align_set(glEo, EVAS_HINT_FILL, EVAS_HINT_FILL);
                elm_genlist_mode_set(glEo, ELM_LIST_COMPRESS);
                elm_genlist_homogeneous_set(glEo, EINA_TRUE);
 
-               m_genlist = makeShared<StyledWidget>(glEo);
-               m_circleEo = eext_circle_object_genlist_add(m_genlist->getEo(), circleSurf);
+               m_genlist = makeShared<StyledWidget>(glEo, true);
+               m_genlist->setWeight(EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+               m_genlist->setAlign(EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+               m_circleEo = eext_circle_object_genlist_add(m_genlist->getEo(),
+                               circleSurf);
                if (!m_circleEo) {
                        LOG_RETURN(RES_FAIL, "elm_genlist_add() failed!");
                }
-               eext_circle_object_genlist_scroller_policy_set(m_circleEo, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
+               eext_circle_object_genlist_scroller_policy_set(m_circleEo,
+                               ELM_SCROLLER_POLICY_OFF,
+                               ELM_SCROLLER_POLICY_AUTO);
                deactivateRotary();
 
                FAIL_RETURN(fillGenlist(), "fillGenlist() failed!");
@@ -325,6 +347,8 @@ namespace callui {
 
                m_panelLy->setContent(*m_genlist, impl::PART_SWL_CONTENT);
 
+               registerGenlistAtspiGestureCallbacks();
+
                return RES_OK;
        }
 
@@ -344,7 +368,7 @@ namespace callui {
 
        Result RejectMsgPresenter::addGenlistTitleItem()
        {
-               static auto titleItc = createGenlistItemClass("title",
+               static auto titleItc = utils::createGenlistItemClass("title",
                                [](void *data, Evas_Object *obj, const char *part) -> char * {
                                        return strdup(STR_DECLINE_MESSAGES.translate());
                                });
@@ -364,7 +388,7 @@ namespace callui {
 
        Result RejectMsgPresenter::addGenlistTextItem(const IRejectMsgSRef &rm)
        {
-               static auto textItc = createGenlistItemClass("1text.1icon",
+               static auto textItc = utils::createGenlistItemClass("1text.1icon",
                                [](void *data, Evas_Object *obj, const char *part) -> char * {
                                        if (!data) {
                                                LOG_RETURN_VALUE(RES_FAIL, nullptr, "Data is NULL");
@@ -400,7 +424,7 @@ namespace callui {
 
        Result RejectMsgPresenter::addGenlistBottomItem()
        {
-               static auto paddingItc = createGenlistItemClass("1text.1icon");
+               static auto paddingItc = utils::createGenlistItemClass("1text.1icon");
 
                auto *item = elm_genlist_item_append(*m_genlist, &paddingItc,
                                nullptr,
@@ -445,6 +469,15 @@ namespace callui {
                return m_state;
        }
 
+       void RejectMsgPresenter::showPanel()
+       {
+               DLOG();
+               if (m_state != RejectMsgState::SHOWN) {
+                       DLOG("Panel state [NOT SHOWN]");
+                       elm_panel_hidden_set(m_panel->getEo(), EINA_FALSE);
+               }
+       }
+
        void RejectMsgPresenter::hidePanel()
        {
                DLOG();
@@ -493,4 +526,77 @@ namespace callui {
                }
        }
 
+       // Screen Reader
+       Elm_Interface_Atspi_Accessible *RejectMsgPresenter::getFirstAo()
+       {
+               return (m_genlist) ?
+                               elm_genlist_first_item_get(*m_genlist) :
+                               nullptr;
+       }
+
+       Elm_Interface_Atspi_Accessible *RejectMsgPresenter::getLastAo()
+       {
+               return (m_genlist) ?
+                               elm_genlist_last_item_get(*m_genlist) :
+                               nullptr;
+       }
+
+       Result RejectMsgPresenter::createAtspiHighlightHelper()
+       {
+               m_atspiHelper = AtspiHighlightHelper::newInstance(*this, getWindow());
+               if (!m_atspiHelper) {
+                       LOG_RETURN(RES_FAIL, "AtspiHighlightHelper::newInstance() failed!");
+               }
+
+               m_atspiHelper->setRelationEventHandler(WEAK_DELEGATE(
+                               RejectMsgPresenter::onAtspiHighlight, asWeak(*this)));
+
+               return RES_OK;
+       }
+
+       void RejectMsgPresenter::registerGenlistAtspiGestureCallbacks()
+       {
+               auto item = getFirstAo();
+               elm_atspi_accessible_gesture_cb_set(item,
+                               CALLBACK_A(RejectMsgPresenter::onAtspiGesture), this);
+
+               item = getLastAo();
+               elm_atspi_accessible_gesture_cb_set(item,
+                               CALLBACK_A(RejectMsgPresenter::onAtspiGesture), this);
+       }
+
+       Elm_Interface_Atspi_Accessible *RejectMsgPresenter::onAtspiHighlight(
+                       Elm_Interface_Atspi_Accessible *ao,
+                       Elm_Atspi_Relation_Type flowRelation)
+       {
+               DLOG("ENTER");
+
+               DLOG("FlowRelation [%s]",
+                               flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM ?
+                                               "FROM" :
+                                               "TO");
+
+               if (ao == getFirstAo()) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_TO) {
+                               return nullptr;
+                       }
+               } else if (ao == getLastAo()) {
+                       if (flowRelation == ELM_ATSPI_RELATION_FLOWS_FROM) {
+                               return nullptr;
+                       }
+               } else if (ao == getWindow()) {
+                       return getFirstAo();
+               } else {
+                       LOG_RETURN_VALUE(RES_FAIL, nullptr, "Unknown object!");
+               }
+               return ao;
+       }
+
+       Eina_Bool RejectMsgPresenter::onAtspiGesture(
+                       Elm_Atspi_Gesture_Info gestureInfo,
+                       Elm_Interface_Atspi_Accessible *ao)
+       {
+               return toEina(
+                               m_atspiHelper->handleGesture(ao, gestureInfo));
+       }
 }
index d07af888ec2a5b877df72b17e10dda4e4f80ce64..49f910eed1a702a513d9df5b21e10dce3758e507 100644 (file)
@@ -29,6 +29,7 @@
 
 namespace callui {
        constexpr auto CALL_VC_TIMER_INTERVAL = 1.5;
+       constexpr auto CALL_VC_SCREEN_READER_TIMER_INTERVAL = 5.0;
        constexpr auto VOLUME_LEVEL_MIN = 1;
 }
 
index ef4ea0239293e15f1b47af0e0fbd4a5013f40029..adc682dcaea7af69999ab3ec87f9f96e1f22e3b8 100644 (file)
@@ -41,9 +41,7 @@ namespace callui {
                }
        }
 
-       void setCallDuration(const struct tm &time,
-                       EdjeWidget &widget,
-                       const EdjePart &part)
+       TString getCallDuration(const struct tm &time)
        {
                TString tmp;
                if (time.tm_hour > 0) {
@@ -51,7 +49,7 @@ namespace callui {
                } else {
                        tmp = himpl::STR_MM_SS_TIME.format(time.tm_min, time.tm_sec);
                }
-               widget.setText(tmp, part);
+               return tmp;
        }
 
        void tryUpdateCallDurationTime(
@@ -62,7 +60,8 @@ namespace callui {
        {
                if ((compTime.tm_sec - curTime.tm_sec) != 0) {
                        curTime = compTime;
-                       setCallDuration(curTime, widget, part);
+                       auto tmp = getCallDuration(curTime);
+                       widget.setText(tmp, part);
                }
        }
 
index cc46cf7bc6fc44d0efe3ab7cb186154938a0b3d3..9a0081c424398ed9fb66cd604765ff689b25936a 100644 (file)
@@ -28,8 +28,7 @@ namespace callui {
        void replaceSubstringInString(std::string &str,
                        const std::string &from, const std::string &to);
 
-       void setCallDuration(const struct tm &time,
-                       ucl::EdjeWidget &widget, const ucl::EdjePart &part);
+       ucl::TString getCallDuration(const struct tm &time);
 
        void tryUpdateCallDurationTime(
                        struct tm &curTime, struct tm &compTime,
index e2258e7d2766aff998cce3b5dc5c41f7c71a0273..fbac1f1fecf083ae6693f5f52244298571312b2b 100644 (file)
@@ -48,4 +48,27 @@ namespace callui {
        const ucl::TString STR_MORE_HOLD {"Hold"};
        const ucl::TString STR_MORE_UNHOLD {"Unhold"};
        const ucl::TString STR_MORE_GEAR {"Gear"};
+
+       // Screen Reader
+       const ucl::TString AO_STR_CALL {"Call"};
+       const ucl::TString AO_STR_VOLUME {"Volume"};
+       const ucl::TString AO_STR_HEADSET {"Headset"};
+       const ucl::TString AO_STR_GEAR_SPK {"Gear speaker"};
+       const ucl::TString AO_STR_MUTE {"Mute"};
+       const ucl::TString AO_STR_MORE_OPTIONS {"More options"};
+       const ucl::TString AO_STR_END_CALL {"End call"};
+       const ucl::TString AO_STR_CALLBACK {"Callback"};
+       const ucl::TString AO_STR_ADD_TO_CONTACTS {"Add to contacts"};
+       const ucl::TString AO_STR_ROTATE_BEZEL_TO_ADJUST {"Rotate bezel to adjust"};
+       const ucl::TString AO_STR_DECREASE_VOLUME {"Decrease volume"};
+       const ucl::TString AO_STR_INCREASE_VOLUME {"Increase volume"};
+       const ucl::TString AO_STR_ACCEPT_CALL {"Accept call"};
+       const ucl::TString AO_STR_SWIPE_RIGHT_WITH_TWO_FINGERS_TO_ACCEPT
+               {"Swipe right with two fingers to accept"};
+       const ucl::TString AO_STR_REJECT_CALL {"Reject call"};
+       const ucl::TString AO_STR_SWIPE_LEFT_WITH_TWO_FINGERS_TO_REJECT
+               {"Swipe left with two fingers to reject"};
+       const ucl::TString AO_STR_DECLINE_MESSAGES {"Decline messages"};
+       const ucl::TString AO_STR_SWIPE_UP_WITH_TWO_FINGERS_TO_SEND_A_DECLINE_MESSAGE
+               {"Swipe up with two fingers to send a decline message"};
 }
index f8841a77a307f0c1a6a8de8460c8b669ae5f9ae6..0049890386be46b128e36b9a18ba7a2bc01814b5 100644 (file)
@@ -17,6 +17,7 @@
 #include "view/AcceptRejectWidget.h"
 
 #include "common.h"
+#include "resources.h"
 
 #define CU_COL_TRANSPARENT  0, 0, 0, 0
 #define CU_COL_WHITE        255, 255, 255, 255
@@ -205,6 +206,9 @@ namespace callui {
                auto result = makeShared<AcceptRejectWidget>(layout,
                                m_acceptHandler, m_rejectHandler, m_acceptBtnType);
 
+               FAIL_RETURN_VALUE(result->registerAccessObjects(parent), {},
+                               "registerScreenReaderObjects() failed!");
+
                result->bindToEo();
 
                return result;
@@ -1396,4 +1400,45 @@ namespace callui {
 
                setRejectUnpressedTransitions();
        }
+
+       // Screen Reader
+       ElmWidget *AcceptRejectWidget::getAcceptAo()
+       {
+               return m_accAo.get();
+       }
+
+       ElmWidget *AcceptRejectWidget::getRejectAo()
+       {
+               return m_rejAo.get();
+       }
+
+       Result AcceptRejectWidget::registerAccessObjects(ElmWidget &parent)
+       {
+               m_accAo = utils::createAccessObject(parent, *m_accEventLy);
+               if (!m_accAo) {
+                       LOG_RETURN(RES_FAIL, "createAccessObject() failed!");
+               }
+               elm_atspi_accessible_translation_domain_set(*m_accAo, PACKAGE);
+               elm_atspi_accessible_reading_info_type_set(*m_accAo,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME |
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION);
+               elm_atspi_accessible_name_set(*m_accAo, AO_STR_ACCEPT_CALL);
+               elm_atspi_accessible_description_set(*m_accAo,
+                               AO_STR_SWIPE_RIGHT_WITH_TWO_FINGERS_TO_ACCEPT);
+
+               m_rejAo = utils::createAccessObject(parent, *m_rejEventLy);
+               if (!m_rejAo) {
+                       LOG_RETURN(RES_FAIL, "createAccessObject() failed!");
+               }
+               elm_atspi_accessible_translation_domain_set(*m_rejAo, PACKAGE);
+               elm_atspi_accessible_reading_info_type_set(*m_rejAo,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME |
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION);
+               elm_atspi_accessible_name_set(*m_rejAo, AO_STR_REJECT_CALL);
+               elm_atspi_accessible_description_set(*m_rejAo,
+                               AO_STR_SWIPE_LEFT_WITH_TWO_FINGERS_TO_REJECT);
+
+               return RES_OK;
+       }
+
 }
index 9e22cd6413305dcc7e0b23d571f9f0020bc722e4..17205e3e35ff37f650cfc946efc40075d5702043 100644 (file)
@@ -88,10 +88,8 @@ namespace callui {
        {
                m_layout->setIsOwner(false);
 
-               evas_object_size_hint_align_set(m_slider,
-                               EVAS_HINT_FILL, EVAS_HINT_FILL);
-               evas_object_size_hint_weight_set(m_slider,
-                               EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+               m_slider.setAlign(EVAS_HINT_FILL, EVAS_HINT_FILL);
+               m_slider.setWeight(EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
 
                m_layout->setContent(m_circleLy, impl::PART_SWL_SLIDER);
                show(m_circleLy);
@@ -100,6 +98,14 @@ namespace callui {
 
                setMaxValue(maxValue);
                setValue(curValue);
+
+               // Screen Reader
+               elm_atspi_accessible_role_set(m_slider,
+                               ELM_ATSPI_ROLE_REDUNDANT_OBJECT);
+               elm_atspi_accessible_role_set(m_circleLy,
+                               ELM_ATSPI_ROLE_REDUNDANT_OBJECT);
+               elm_atspi_accessible_role_set(*m_layout,
+                               ELM_ATSPI_ROLE_REDUNDANT_OBJECT);
        }
 
        void Slider::setValue(int value)
index 3cba34dff17ba560714c488afbcb8c47538dc8ec..355aae121374d126f0656e52de00e499966da68e 100644 (file)
@@ -17,6 +17,7 @@
 #include "view/VolumeControl.h"
 
 #include "common.h"
+#include "resources.h"
 
 namespace callui { namespace { namespace impl {
 
@@ -33,6 +34,7 @@ namespace callui { namespace { namespace impl {
 
        constexpr EdjePart PART_TXT_INFO {"txt.info"};
        constexpr EdjePart PART_TXT_VALUE {"txt.value"};
+       constexpr EdjePart PART_TXT_VALUE_AO {"ao_txt.value"};
 
 }}}
 
@@ -110,8 +112,13 @@ namespace callui {
        {
        }
 
-       void VolumeControl::prepare(const ucl::TString &info, int curValue)
+       void VolumeControl::prepare(const TString &info, int curValue)
        {
+               m_layout->addEventHandler(WidgetEvent::SHOW, WEAK_DELEGATE(
+                               VolumeControl::onWidgetShowCb, asWeak(*this)));
+               m_layout->addEventHandler(WidgetEvent::HIDE, WEAK_DELEGATE(
+                               VolumeControl::onWidgetHideCb, asWeak(*this)));
+
                m_decreaseBtn.setStyle(impl::STYLE_BTN_MINUS);
                m_decreaseBtn.addEventHandler(BTN_CLICKED, WEAK_DELEGATE(
                                VolumeControl::onDecreaseBtnClickedCb, asWeak(*this)));
@@ -126,6 +133,20 @@ namespace callui {
 
                setInfoText(info);
                setValue(curValue);
+
+               registerAccessObjectInformation();
+       }
+
+       void VolumeControl::onWidgetShowCb(Widget &widget, void *eventInfo)
+       {
+               if (m_valueTxtAo)
+                       show(*m_valueTxtAo);
+       }
+
+       void VolumeControl::onWidgetHideCb(Widget &widget, void *eventInfo)
+       {
+               if (m_valueTxtAo)
+                       hide(*m_valueTxtAo);
        }
 
        void VolumeControl::setInfoText(const TString &info)
@@ -169,4 +190,65 @@ namespace callui {
                }
        }
 
+       // Screen Reader
+       ElmWidget *VolumeControl::getDecreaseBtn()
+       {
+               return &m_decreaseBtn;
+       }
+
+       ElmWidget *VolumeControl::getIncreaseBtn()
+       {
+               return &m_increaseBtn;
+       }
+
+       ElmWidget *VolumeControl::getValueTxtAo()
+       {
+               return m_valueTxtAo.get();
+       }
+
+       void VolumeControl::registerAccessObjectInformation()
+       {
+               elm_atspi_accessible_translation_domain_set(m_slider, PACKAGE);
+               elm_atspi_accessible_reading_info_type_set(m_slider,
+                               ELM_ACCESSIBLE_READING_INFO_TYPE_NAME
+                               | ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION);
+               elm_atspi_accessible_name_set(m_slider, AO_STR_VOLUME);
+               elm_atspi_accessible_description_set(m_slider,
+                               AO_STR_ROTATE_BEZEL_TO_ADJUST);
+
+               elm_atspi_accessible_translation_domain_set(m_decreaseBtn, PACKAGE);
+               elm_atspi_accessible_name_set(m_decreaseBtn,
+                               AO_STR_DECREASE_VOLUME);
+
+               elm_atspi_accessible_translation_domain_set(m_increaseBtn, PACKAGE);
+               elm_atspi_accessible_name_set(m_increaseBtn,
+                               AO_STR_INCREASE_VOLUME);
+
+               m_valueTxtAo = utils::createAccessObjectFromLyPart(
+                               *m_layout,
+                               *m_layout,
+                               impl::PART_TXT_VALUE_AO);
+               if (!m_valueTxtAo) {
+                       ELOG("createAccessObjectFromLyPart() failed");
+               } else {
+                       elm_atspi_accessible_translation_domain_set(*m_valueTxtAo,
+                                       PACKAGE);
+                       elm_atspi_accessible_reading_info_type_set(*m_valueTxtAo,
+                                       ELM_ACCESSIBLE_READING_INFO_TYPE_NAME);
+                       elm_atspi_accessible_name_cb_set(*m_valueTxtAo,
+                                       [](void *data, Evas_Object *obj) -> char *
+                                       {
+                                               auto self = static_cast<VolumeControl *>(data);
+                                               if (!self) {
+                                                       return nullptr;
+                                               }
+                                               auto txt = self->m_layout->
+                                                               getText(impl::PART_TXT_VALUE).getCStr();
+                                               return (txt) ? strdup(txt) : nullptr;
+                                       },
+                                       this);
+               }
+
+               elm_atspi_accessible_role_set(*m_layout, ELM_ATSPI_ROLE_TEXT);
+       }
 }
index e4f85954e0117afdfd3b06e5544d416cfb1fa609..6f6a196b585241c5e2a1d6f1508675acfe513c25 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "ucl/gui/Window.h"
 #include "ucl/gui/Naviframe.h"
+#include "ucl/gui/Layout.h"
 
 #include "common.h"
 
@@ -26,9 +27,12 @@ namespace callui { namespace { namespace impl {
        using namespace ucl;
 
        constexpr EoDataKey CIRCLE_SURFACE {"callui,eext,circle,surface"};
+
+       constexpr LayoutTheme LAYOUT_FAKE_ACCESS_OBJECT
+                       {"layout", "callui", "fake_access_object"};
 }}}
 
-namespace callui {
+namespace callui { namespace utils {
 
        using namespace ucl;
 
@@ -72,26 +76,33 @@ namespace callui {
                return sfc;
        }
 
-       void addRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data)
-       {
-               eext_rotary_event_handler_add(func, data);
-       }
-
-       void delRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data)
+       ElmWidgetSRef createFakeAccessObject(ElmWidget &parent)
        {
-               std::vector<void *> backup;
-               while (true) {
-                       void *const oldData = eext_rotary_event_handler_del(func);
-                       if (!oldData || (oldData == data)) {
-                               return;
-                       }
-                       backup.push_back(oldData);
-               }
-               for (auto i = backup.size(); i-- > 0; ) {
-                       eext_rotary_event_handler_add(func, backup[i]);
+               const auto result = Layout::Builder().
+                               setTheme(impl::LAYOUT_FAKE_ACCESS_OBJECT).
+                               setIsOwner(true).
+                               setNeedBindToEo(true).
+                               build(parent);
+               if (!result) {
+                       LOG_RETURN_VALUE(RES_FAIL, {}, "Layout::build() failed!");
                }
+
+               result->setGeometry(0, 0, 1, 1);
+               show(*result);
+
+               elm_atspi_accessible_reading_info_type_set(*result, 0);
+
+               elm_atspi_accessible_gesture_cb_set(*result,
+                       [](void *, Elm_Atspi_Gesture_Info, Evas_Object *) -> Eina_Bool
+                       {
+                               return EINA_TRUE;
+                       },
+                       nullptr);
+
+               return result;
        }
 
+
        Elm_Genlist_Item_Class createGenlistItemClass(const char *style,
                        Elm_Gen_Item_Text_Get_Cb txtCb,
                        Elm_Gen_Item_Content_Get_Cb contentCb,
@@ -108,10 +119,85 @@ namespace callui {
                return itc;
        }
 
+       ElmWidgetSRef createAccessObject(ElmWidget &parent, Widget &ly)
+       {
+               auto ao = elm_access_object_register(ly, parent);
+               if (!ao) {
+                       LOG_RETURN_VALUE(RES_FAIL, {},
+                                       "elm_access_object_register() failed!");
+               }
+
+               return makeShared<ElmWidget>(ao, true);
+       }
+
+       ElmWidgetSRef createAccessObjectFromLyPart(ElmWidget &parent,
+                       Widget &ly,
+                       const EdjePart &lyPart)
+       {
+               auto po = const_cast<Evas_Object *>(
+                               edje_object_part_object_get(
+                                               elm_layout_edje_get(ly), lyPart));
+               if (!po) {
+                       LOG_RETURN_VALUE(RES_FAIL, {}, "Part object is NULL");
+               }
+
+               auto ao = elm_access_object_register(po, parent);
+               if (!ao) {
+                       LOG_RETURN_VALUE(RES_FAIL, {},
+                                       "elm_access_object_register() failed!");
+               }
+
+               return makeShared<ElmWidget>(ao, true);
+       }
+
+       void destroyAccessObject(ElmWidget &ao)
+       {
+               elm_access_object_unregister(ao);
+       }
+
+}}
+
+namespace callui {
+
+       void addRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data)
+       {
+               eext_rotary_event_handler_add(func, data);
+       }
+
+       void delRotaryEventHandler(Eext_Rotary_Handler_Cb func, void *data)
+       {
+               std::vector<void *> backup;
+               while (true) {
+                       void *const oldData = eext_rotary_event_handler_del(func);
+                       if (!oldData || (oldData == data)) {
+                               break;
+                       }
+                       backup.push_back(oldData);
+               }
+               for (auto i = backup.size(); i-- > 0; ) {
+                       eext_rotary_event_handler_add(func, backup[i]);
+               }
+       }
+
        LayoutTheme getImageTheme(const char *const fileName)
        {
                return {"layout", "callui_image", fileName};
        }
 
+       Elm_Atspi_Relation_Type getFlowRelation(Elm_Atspi_Gesture_Info gestureInfo)
+       {
+               switch (gestureInfo.type) {
+               case ELM_ATSPI_GESTURE_ONE_FINGER_FLICK_RIGHT:
+               case ELM_ATSPI_GESTURE_ONE_FINGER_FLICK_DOWN:
+                       return ELM_ATSPI_RELATION_FLOWS_TO;
+               case ELM_ATSPI_GESTURE_ONE_FINGER_FLICK_LEFT:
+               case ELM_ATSPI_GESTURE_ONE_FINGER_FLICK_UP:
+                       return ELM_ATSPI_RELATION_FLOWS_FROM;
+               default:
+                       break;
+               }
+               return ELM_ATSPI_RELATION_NULL;
+       }
+
 }