[ElmSharp.Wearable] Add Rotary Event Manager 45/146745/14
authorpius.lee <pius.lee@samsung.com>
Wed, 30 Aug 2017 08:32:42 +0000 (17:32 +0900)
committerpius.lee <pius.lee@samsung.com>
Tue, 12 Sep 2017 08:18:51 +0000 (17:18 +0900)
Add Global event for Rotary device.
Add Rotary Event functions for isolated evas object.
Add Tests for rotary events.

Change-Id: I56fc247daf0b25c93269a09310c22ddb257acc6e

src/ElmSharp.Wearable/ElmSharp.Wearable/RotaryEventManager.cs [new file with mode: 0644]
src/ElmSharp.Wearable/Interop/Interop.Eext.Rotary.cs
test/ElmSharp.Wearable.Test/TC/CircleTool.cs [new file with mode: 0644]
test/ElmSharp.Wearable.Test/TC/RotaryEvent1.cs [new file with mode: 0644]
test/ElmSharp.Wearable.Test/TC/RotaryEvent2.cs [new file with mode: 0644]
test/ElmSharp.Wearable.Test/TC/RotaryEvent3.cs [new file with mode: 0644]

diff --git a/src/ElmSharp.Wearable/ElmSharp.Wearable/RotaryEventManager.cs b/src/ElmSharp.Wearable/ElmSharp.Wearable/RotaryEventManager.cs
new file mode 100644 (file)
index 0000000..d7fb66b
--- /dev/null
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace ElmSharp.Wearable
+{
+    /// <summary>
+    /// RotaryEventManager serve functions for global Rotary event like Galaxy Gear.
+    /// </summary>
+    public static class RotaryEventManager
+    {
+        static Dictionary<RotaryEventHandler, Interop.Eext.Eext_Rotary_Handler_Cb> s_rotaryEventHandlers = new Dictionary<RotaryEventHandler, Interop.Eext.Eext_Rotary_Handler_Cb>();
+
+        /// <summary>
+        /// Rotated will triggered when rotatable device like Galaxy Gear Bezel is rotated.
+        /// </summary>
+        public static event RotaryEventHandler Rotated
+        {
+            add
+            {
+                if (s_rotaryEventHandlers.ContainsKey(value)) return;
+
+                Interop.Eext.Eext_Rotary_Handler_Cb cb = (data, infoPtr) =>
+                {
+                    var info = Interop.Eext.FromIntPtr(infoPtr);
+                    value.Invoke(new RotaryEventArgs
+                    {
+                        IsClockwise = info.Direction == Interop.Eext.Eext_Rotary_Event_Direction.Clockwise,
+                        Timestamp = info.TimeStamp
+                    });
+                    return true;
+                };
+                Interop.Eext.eext_rotary_event_handler_add(cb, IntPtr.Zero);
+                s_rotaryEventHandlers[value] = cb;
+            }
+
+            remove
+            {
+                Interop.Eext.Eext_Rotary_Handler_Cb cb;
+                if (s_rotaryEventHandlers.TryGetValue(value, out cb))
+                {
+                    Interop.Eext.eext_rotary_event_handler_del(cb);
+                    s_rotaryEventHandlers.Remove(value);
+                }
+            }
+        }
+    }
+
+
+    /// <summary>
+    /// RotaryEventManager serve extension functions for Rotary event to EvasObject on device like Galaxy Gear.
+    /// </summary>
+    public static class EvasObjectExtensions
+    {
+        static Dictionary<EvasObject, RotaryEventHandler> s_rotaryObjectEventHandlers = new Dictionary<EvasObject, RotaryEventHandler>();
+        static Dictionary<EvasObject, Interop.Eext.Eext_Rotary_Event_Cb> s_rotaryObjectEventMap = new Dictionary<EvasObject, Interop.Eext.Eext_Rotary_Event_Cb>();
+
+        /// <summary>
+        /// Add a handler for Rotary event on specific EvasObject.
+        /// </summary>
+        /// <param name="obj">Target EvasObject</param>
+        /// <param name="handler">Event handler for Rotary event</param>
+        public static void AddRotaryEventHandler(this EvasObject obj, RotaryEventHandler handler)
+        {
+            EnableRotaryEventHandler(obj);
+
+            if (s_rotaryObjectEventHandlers.ContainsKey(obj))
+            {
+                s_rotaryObjectEventHandlers[obj] += handler;
+            }
+            else
+            {
+                s_rotaryObjectEventHandlers[obj] = handler;
+            }
+        }
+
+        /// <summary>
+        /// Remove a handler on specific EvasObject for Rotary event.
+        /// </summary>
+        /// <param name="obj">Target EvasObject</param>
+        /// <param name="handler">Event handler for Rotary event</param>
+        public static void RemoveRotaryEventHandler(this EvasObject obj, RotaryEventHandler handler)
+        {
+            if (s_rotaryObjectEventHandlers.ContainsKey(obj))
+            {
+                s_rotaryObjectEventHandlers[obj] -= handler;
+                if (s_rotaryObjectEventHandlers[obj] == null)
+                {
+                    DisableRotaryEventHandler(obj, false);
+                    s_rotaryObjectEventHandlers.Remove(obj);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Activate this object can take Rotary event.
+        /// </summary>
+        /// <param name="obj">Target object</param>
+        public static void Activate(this EvasObject obj)
+        {
+            Interop.Eext.eext_rotary_object_event_activated_set(obj, true);
+        }
+
+        /// <summary>
+        /// Deactivate this object is blocked from Rotary event.
+        /// </summary>
+        /// <param name="obj">Target object</param>
+        public static void Deactivate(this EvasObject obj)
+        {
+            Interop.Eext.eext_rotary_object_event_activated_set(obj, false);
+        }
+
+        static void EnableRotaryEventHandler(EvasObject obj)
+        {
+            if (!s_rotaryObjectEventMap.ContainsKey(obj))
+            {
+                Interop.Eext.Eext_Rotary_Event_Cb cb = (d, o, i) =>
+                {
+                    RotaryEventHandler events;
+                    if (s_rotaryObjectEventHandlers.TryGetValue(obj, out events))
+                    {
+                        var info = Interop.Eext.FromIntPtr(i);
+                        events?.Invoke(new RotaryEventArgs
+                        {
+                            IsClockwise = info.Direction == Interop.Eext.Eext_Rotary_Event_Direction.Clockwise,
+                            Timestamp = info.TimeStamp
+                        });
+                    }
+                    return true;
+                };
+                Interop.Eext.eext_rotary_object_event_callback_add(obj, cb, IntPtr.Zero);
+                s_rotaryObjectEventMap[obj] = cb;
+                obj.Deleted += (s, e) => DisableRotaryEventHandler(obj, true);
+            }
+        }
+
+        static void DisableRotaryEventHandler(EvasObject obj, bool removeHandler)
+        {
+            Interop.Eext.Eext_Rotary_Event_Cb cb;
+            if (s_rotaryObjectEventMap.TryGetValue(obj, out cb))
+            {
+                Interop.Eext.eext_rotary_object_event_callback_del(obj, cb);
+                s_rotaryObjectEventMap.Remove(obj);
+            }
+            if (removeHandler && s_rotaryObjectEventHandlers.ContainsKey(obj))
+            {
+                s_rotaryObjectEventHandlers.Remove(obj);
+            }
+        }
+    }
+
+    /// <summary>
+    /// Handler for Rotary event
+    /// </summary>
+    /// <param name="args">Rotary event information</param>
+    public delegate void RotaryEventHandler(RotaryEventArgs args);
+
+    /// <summary>
+    /// RotaryEventArgs serve information for triggered rotary event.
+    /// </summary>
+    public class RotaryEventArgs : EventArgs
+    {
+        /// <summary>
+        /// IsClockwise is true when Rotary device rotated clockwise direction or false on counter clockwise.
+        /// </summary>
+        public bool IsClockwise { get; set; }
+
+        /// <summary>
+        /// Timestamp of rotary event
+        /// </summary>
+        public uint Timestamp { get; set; }
+    }
+}
index 5348d39..619077d 100644 (file)
@@ -19,10 +19,49 @@ using System.Runtime.InteropServices;
 
 internal static partial class Interop
 {
-
     internal static partial class Eext
     {
+        const short EEXT_CALLBACK_PRIORITY_AFTER = 100;
+        const short EEXT_CALLBACK_PRIORITY_BEFORE = -100;
+        const short EEXT_CALLBACK_PRIORITY_DEFAULT = 0;
+
+        internal delegate bool Eext_Rotary_Event_Cb(IntPtr data, IntPtr obj, IntPtr info);
+        internal delegate bool Eext_Rotary_Handler_Cb(IntPtr data, IntPtr info);
+
+        internal enum Eext_Rotary_Event_Direction
+        {
+            Clockwise,
+            CounterClockwise
+        }
+
+        internal struct Eext_Rotary_Event_Info
+        {
+            public Eext_Rotary_Event_Direction Direction;
+            public uint TimeStamp;
+        }
+
+        internal static Eext_Rotary_Event_Info FromIntPtr(IntPtr infoPtr)
+        {
+            var info = Marshal.PtrToStructure<Eext_Rotary_Event_Info>(infoPtr);
+            return info;
+        }
+
         [DllImport(Libraries.Eext)]
         internal static extern IntPtr eext_rotary_object_event_activated_set(IntPtr circleObject, bool activated);
+
+        [DllImport(Libraries.Eext)]
+        internal static extern bool eext_rotary_object_event_callback_add(IntPtr obj, Eext_Rotary_Event_Cb func, IntPtr data);
+
+        [DllImport(Libraries.Eext)]
+        internal static extern bool eext_rotary_object_event_callback_priority_add(IntPtr obj, short priority, Eext_Rotary_Event_Cb func, IntPtr data);
+
+        [DllImport(Libraries.Eext)]
+        internal static extern IntPtr eext_rotary_object_event_callback_del(IntPtr obj, Eext_Rotary_Event_Cb func);
+
+        [DllImport(Libraries.Eext)]
+        internal static extern bool eext_rotary_event_handler_add(Eext_Rotary_Handler_Cb func, IntPtr data);
+
+        [DllImport(Libraries.Eext)]
+        internal static extern IntPtr eext_rotary_event_handler_del(Eext_Rotary_Handler_Cb func);
     }
 }
\ No newline at end of file
diff --git a/test/ElmSharp.Wearable.Test/TC/CircleTool.cs b/test/ElmSharp.Wearable.Test/TC/CircleTool.cs
new file mode 100644 (file)
index 0000000..b12b507
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ElmSharp.Test
+{
+    public static class CircleTool
+    {
+        static Rect _inSquare;
+        public static Rect GetInnerSquare(this Window window)
+        {
+            Size screenSize = window.ScreenSize;
+            int min = Math.Min(screenSize.Height, screenSize.Width);
+            int width = (int)(min * Math.Cos(Math.PI / 4));
+            int x = screenSize.Width / 2 - width / 2;
+            int y = screenSize.Height / 2 - width / 2;
+
+            return _inSquare == default(Rect) ? (_inSquare = new Rect(x, y, width, width)) : _inSquare;
+        }
+    }
+}
diff --git a/test/ElmSharp.Wearable.Test/TC/RotaryEvent1.cs b/test/ElmSharp.Wearable.Test/TC/RotaryEvent1.cs
new file mode 100644 (file)
index 0000000..a4faf1f
--- /dev/null
@@ -0,0 +1,55 @@
+using ElmSharp.Wearable;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ElmSharp.Test.TC
+{
+    public class RotaryEvent1 : TestCaseBase
+    {
+        public override string TestName => "Rotary Event Test 1";
+
+        public override string TestDescription => "Wearable test for Rotary event";
+
+        public override void Run(Window window)
+        {
+            Log.Debug("window id is " + window.Handle.ToString());
+            Rect square = window.GetInnerSquare();
+
+            Log.Debug(square.ToString());
+
+            Rectangle redSquare = new Rectangle(window)
+            {
+                Color = Color.Red,
+                Geometry = square
+            };
+            redSquare.Show();
+
+            double degrees = 0;
+
+            RotaryEventHandler handler = (args) =>
+            {
+                if (args.IsClockwise) degrees += 10;
+                else degrees -= 10;
+
+                if (degrees < 0) degrees = 360;
+                else if (degrees > 360) degrees = 0;
+
+                Rect rect = redSquare.Geometry;
+                EvasMap map = new EvasMap(4);
+                map.PopulatePoints(redSquare, 0);
+                map.Rotate(degrees, rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
+                redSquare.EvasMap = map;
+                redSquare.IsMapEnabled = true;
+            };
+
+            RotaryEventManager.Rotated += handler;
+
+            window.BackButtonPressed += (s, e) =>
+            {
+                RotaryEventManager.Rotated -= handler;
+                Log.Debug("handler is Removed!!!!!!!");
+            };
+        }
+    }
+}
diff --git a/test/ElmSharp.Wearable.Test/TC/RotaryEvent2.cs b/test/ElmSharp.Wearable.Test/TC/RotaryEvent2.cs
new file mode 100644 (file)
index 0000000..8893d57
--- /dev/null
@@ -0,0 +1,53 @@
+using ElmSharp.Wearable;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ElmSharp.Test.TC
+{
+    public class RotaryEvent2 : TestCaseBase
+    {
+        public override string TestName => "Rotary Event Test 2";
+
+        public override string TestDescription => "Wearable test for Rotary object event";
+
+        Rectangle rect;
+
+        double degrees = 0;
+
+        void EventHandler(RotaryEventArgs args)
+        {
+            if (args.IsClockwise) degrees += 10;
+            else degrees -= 10;
+
+            if (degrees < 0) degrees = 360;
+            else if (degrees > 360) degrees = 0;
+
+            Rect r = rect.Geometry;
+            EvasMap map = new EvasMap(4);
+            map.PopulatePoints(rect, 0);
+            map.Rotate(degrees, r.X + r.Width / 2, r.Y + r.Height / 2);
+            rect.EvasMap = map;
+            rect.IsMapEnabled = true;
+        }
+
+        public override void Run(Window window)
+        {
+            Rect square = window.GetInnerSquare();
+
+            Log.Debug(square.ToString());
+
+            rect = new Rectangle(window)
+            {
+                Color = Color.Blue,
+                Geometry = square
+            };
+            rect.Show();
+
+            rect.AddRotaryEventHandler(EventHandler);
+            rect.Activate();
+
+            window.BackButtonPressed += (s, e) => rect.RemoveRotaryEventHandler(EventHandler);
+        }
+    }
+}
diff --git a/test/ElmSharp.Wearable.Test/TC/RotaryEvent3.cs b/test/ElmSharp.Wearable.Test/TC/RotaryEvent3.cs
new file mode 100644 (file)
index 0000000..8c439c1
--- /dev/null
@@ -0,0 +1,82 @@
+using ElmSharp.Wearable;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ElmSharp.Test.TC
+{
+    public class RotaryEvent3 : TestCaseBase
+    {
+        public override string TestName => "Rotary Event Test 3";
+
+        public override string TestDescription => "Multiple wearable test for Rotary event";
+
+        public override void Run(Window window)
+        {
+            Log.Debug("window id is " + window.Handle.ToString());
+            Rect square = window.GetInnerSquare();
+
+            Log.Debug(square.ToString());
+
+            Rectangle redSquare = new Rectangle(window)
+            {
+                Color = Color.Red,
+                Geometry = square
+            };
+            redSquare.Show();
+
+            Rectangle blueSquare = new Rectangle(window)
+            {
+                Color = Color.Blue,
+                Geometry = new Rect(square.X + square.Width / 4, square.Y + square.Height / 4, square.Width / 2, square.Height / 2)
+            };
+            blueSquare.Show();
+
+            double degrees = 0;
+            double degrees2 = 0;
+
+            RotaryEventHandler handler1 = (args) =>
+            {
+                Log.Debug((args.IsClockwise ? "CW" : "CCW") + " : " + args.Timestamp);
+                if (args.IsClockwise) degrees2 += 10;
+                else degrees2 -= 10;
+
+                if (degrees2 < 0) degrees2 = 360;
+                else if (degrees2 > 360) degrees2 = 0;
+
+                Rect rect = blueSquare.Geometry;
+                EvasMap map = new EvasMap(4);
+                map.PopulatePoints(blueSquare, 0);
+                map.Rotate(degrees2, rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
+                blueSquare.EvasMap = map;
+                blueSquare.IsMapEnabled = true;
+            };
+
+            RotaryEventHandler handler2 = (args) =>
+            {
+                if (args.IsClockwise) degrees += 10;
+                else degrees -= 10;
+
+                if (degrees < 0) degrees = 360;
+                else if (degrees > 360) degrees = 0;
+
+                Rect rect = redSquare.Geometry;
+                EvasMap map = new EvasMap(4);
+                map.PopulatePoints(redSquare, 0);
+                map.Rotate(degrees, rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
+                redSquare.EvasMap = map;
+                redSquare.IsMapEnabled = true;
+            };
+
+            RotaryEventManager.Rotated += handler1;
+            RotaryEventManager.Rotated += handler2;
+
+            window.BackButtonPressed += (s, e) =>
+            {
+                RotaryEventManager.Rotated -= handler1;
+                RotaryEventManager.Rotated -= handler2;
+                Log.Debug("handler is Removed!!!!!!!");
+            };
+        }
+    }
+}