/* * 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 Tizen.Applications; using System.Runtime.InteropServices; namespace Tizen.Applications { /// /// The class for receiving widget events and sending data to the widget. /// /// 3 public class WidgetControl : IDisposable { private const string LogTag = "Tizen.Applications.WidgetControl"; private static Interop.WidgetService.LifecycleCallback _onLifecycleCallback; /// /// Class for the widget instance. /// /// 3 public class Instance { private string _widgetId; internal Instance(string widgetId) { _widgetId = widgetId; } /// /// The widget ID. /// /// 3 public string Id { get; internal set; } /// /// Gets the widget content. /// /// The bundle containing widget content. /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when the required features are not supported. public Bundle GetContent() { IntPtr h; Interop.WidgetService.ErrorCode err = Interop.WidgetService.GetContent(_widgetId, Id, out h); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.IoError: throw new InvalidOperationException("Failed to access DB"); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } return new Bundle(new SafeBundleHandle(h, true)); } /// /// Changes the content for the widget instance. /// /// 3 /// Content to be changed. /// True if you want to update your widget even if the provider is paused, otherwise false. /// http://tizen.org/feature/shell.appwidget /// Thrown when failed because of an invalid argument. /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public void ChangeContent(Bundle content, bool force) { Interop.WidgetService.ErrorCode err = Interop.WidgetService.UpdateContent(_widgetId, Id, content.SafeBundleHandle, force ? 1 : 0); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter"); case Interop.WidgetService.ErrorCode.Canceled: throw new InvalidOperationException("Provider is paused, so this update request is canceld"); case Interop.WidgetService.ErrorCode.OutOfMemory: throw new InvalidOperationException("Out-of-memory at unmanaged code"); case Interop.WidgetService.ErrorCode.Fault: throw new InvalidOperationException("Failed to create a request packet"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } } /// /// Changes the update period for the widget instance. /// /// 3 /// The period to be changed. /// http://tizen.org/feature/shell.appwidget /// Thrown when failed because of an invalid argument. /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public void ChangePeriod(double period) { Interop.WidgetService.ErrorCode err = Interop.WidgetService.ChangePeriod(_widgetId, Id, period); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter"); case Interop.WidgetService.ErrorCode.OutOfMemory: throw new InvalidOperationException("Out-of-memory at unmanaged code"); case Interop.WidgetService.ErrorCode.Fault: throw new InvalidOperationException("Failed to create a request packet"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } } } /// /// The class for the widget size information. /// /// 3 public class Scale { internal Scale() { } /// /// Enumeration for the types of widget size. /// /// 3 public enum SizeType : int { /// /// 175x175 based on 720x1280 resolution. /// Basic1x1 = 0x0001, /// /// 354x175 based on 720x1280 resolution. /// Basic2x1 = 0x0002, /// /// 354x354 based on 720x1280 resolution. /// Basic2x2 = 0x0004, /// /// 712x175 based on 720x1280 resolution. /// Basic4x1 = 0x0008, /// /// 712x354 based on 720x1280 resolution. /// Basic4x2 = 0x0010, /// /// 712x533 based on 720x1280 resolution. /// Basic4x3 = 0x0020, /// /// 712x712 based on 720x1280 resolution. /// Basic4x4 = 0x0040, /// /// 712x891 based on 720x1280 resolution. /// Basic4x5 = 0x0080, /// /// 712x1070 based on 720x1280 resolution. /// Basic4x6 = 0x0100, /// /// 224x215 based on 720x1280 resolution. /// Easy1x1 = 0x1000, /// /// 680x215 based on 720x1280 resolution. /// Easy1x2 = 0x2000, /// /// 680x653 based on 720x1280 resolution. /// Easy1x3 = 0x4000, /// /// 720x1280 based on 720x1280 resolution. /// Full = 0x0800, } /// /// Widget width. /// /// 3 public int Width { get; internal set; } /// ///Widget height. /// /// 3 public int Height { get; internal set; } /// /// The path for the widget preview image file. /// /// 3 public string PreviewImagePath { get; internal set; } /// /// The size type of the widget. /// /// 3 public SizeType Type { get; internal set; } } private event EventHandler _created; private event EventHandler _resumed; private event EventHandler _paused; private event EventHandler _destroyed; private bool _disposedValue = false; private static IDictionary s_lifecycleEventRefCnt = new Dictionary(); private static IList s_eventObjects = new List(); /// /// Factory method for the WidgetControl. /// It will create all the objects of WidgetControl based on the package ID. /// /// 3 /// Package ID. /// The widget control array. /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when failed because of an invalid argument. /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public static WidgetControl[] CreateAll(string pkgId) { List list = new List(); Interop.WidgetService.ErrorCode err = Interop.WidgetService.GetWidgetListByPkgId(pkgId, (widgetId, isPrime, userData) => { list.Add(new WidgetControl(widgetId)); }, IntPtr.Zero); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter"); case Interop.WidgetService.ErrorCode.IoError: throw new InvalidOperationException("Failed to access DB"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } return list.ToArray(); } /// /// Gets all the widget IDs by the package ID. /// /// 3 /// Package ID. /// The widget id array. /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when failed because of an invalid argument. /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public static string[] GetWidgetIds(string pkgId) { List list = new List(); Interop.WidgetService.ErrorCode err = Interop.WidgetService.GetWidgetListByPkgId(pkgId, (widgetId, isPrime, userData) => { list.Add(widgetId); }, IntPtr.Zero); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter"); case Interop.WidgetService.ErrorCode.IoError: throw new InvalidOperationException("Failed to access DB"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } return list.ToArray(); } /// /// Gets main appid of the widget. /// /// 6 /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public string MainAppId { get { string str = Interop.WidgetService.GetWidgetMainAppId(Id); Interop.WidgetService.ErrorCode err = (Interop.WidgetService.ErrorCode)Internals.Errors.ErrorFacts.GetLastResult(); switch (err) { case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } return str; } } /// /// Gets package ID of the widget. /// /// 6 /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public string PackageId { get { string str = Interop.WidgetService.GetWidgetPackageId(Id); Interop.WidgetService.ErrorCode err = (Interop.WidgetService.ErrorCode)Internals.Errors.ErrorFacts.GetLastResult(); switch (err) { case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } return str; } } /// /// The widget ID. /// /// 3 public string Id { get; internal set; } /// /// The flag value for "nodisplay". /// /// 3 /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public bool IsNoDisplay { get { int ret = Interop.WidgetService.GetNoDisplay(Id); Interop.WidgetService.ErrorCode err = (Interop.WidgetService.ErrorCode)Internals.Errors.ErrorFacts.GetLastResult(); switch (err) { case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException("Not supported"); } if (ret != 0) return true; return false; } } /// /// The event handler for a created widget instance. /// /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public event EventHandler Created { add { RegisterLifecycleEvent(); _created += value; } remove { _created -= value; UnregisterLifecycleEvent(); } } /// /// The event handler for a resumed widget instance. /// /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public event EventHandler Resumed { add { RegisterLifecycleEvent(); _resumed += value; } remove { _resumed -= value; UnregisterLifecycleEvent(); } } /// /// The event handler for a paused widget instance. /// /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public event EventHandler Paused { add { RegisterLifecycleEvent(); _paused += value; } remove { _paused -= value; UnregisterLifecycleEvent(); } } /// /// The event handler for a destroyed widget instance. /// /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public event EventHandler Destroyed { add { RegisterLifecycleEvent(); _destroyed += value; } remove { _destroyed -= value; UnregisterLifecycleEvent(); } } /// /// The constructor of the WidgetControl object. /// /// 3 /// Widget ID. public WidgetControl(string widgetId) { Id = widgetId; } /// /// Finalizer of the WidgetControl class. /// ~WidgetControl() { Dispose(false); } /// /// Gets the objects for widget instance information. /// /// The instances list. /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when the API is not supported in this device. /// Thrown when an application does not have the required privileges to access this method. public IEnumerable GetInstances() { IList instances = new List(); Interop.WidgetService.ErrorCode err = Interop.WidgetService.GetInstances(Id, (widgetId, instanceId, userData) => { instances.Add(new Instance(widgetId) { Id = instanceId }); }, IntPtr.Zero); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); } return instances; } /// /// Gets the objects for widget scale information. /// /// The scales list. /// 3 /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public IEnumerable GetScales() { IntPtr wPtr; IntPtr hPtr; IntPtr typesPtr; int[] w; int[] h; int[] types; int cnt1 = 100; int cnt2 = 100; IList scales = new List(); Interop.WidgetService.ErrorCode err = Interop.WidgetService.GetSupportedSizes(Id, ref cnt1, out wPtr, out hPtr); if (cnt1 == 0) { Log.Error(LogTag, "No supported size :" + Id); return null; } switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.IoError: throw new InvalidOperationException("Failed to access DB"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); } w = new int[cnt1]; Marshal.Copy(wPtr, w, 0, cnt1); Interop.Libc.Free(wPtr); h = new int[cnt1]; Marshal.Copy(hPtr, h, 0, cnt1); Interop.Libc.Free(hPtr); err = Interop.WidgetService.GetSupportedSizeTypes(Id, ref cnt2, out typesPtr); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.IoError: throw new InvalidOperationException("Failed to access DB"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); } if (cnt1 != cnt2) { Log.Error(LogTag, "Count not match cnt1 :" + cnt1 + ", cnt2 :" + cnt2); return null; } types = new int[cnt2]; Marshal.Copy(typesPtr, types, 0, cnt2); Interop.Libc.Free(typesPtr); for (int i = 0; i < cnt1; i++) { string prev = Interop.WidgetService.GetPreviewImagePath(Id, types[i]); scales.Add(new Scale() { Width = w[i], Height = h[i], PreviewImagePath = prev, Type = (Scale.SizeType)types[i] }); } return scales; } /// /// Gets the widget name. /// /// 3 /// Language. /// The widget name. /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when the argument is null. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public string GetName(string lang) { if (lang == null) throw new ArgumentNullException(); string str = Interop.WidgetService.GetName(Id, lang); Interop.WidgetService.ErrorCode err = (Interop.WidgetService.ErrorCode)Internals.Errors.ErrorFacts.GetLastResult(); switch (err) { case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); } return str; } /// /// Gets the widget icon path. /// /// 3 /// Language. /// The widget icon path. /// http://tizen.org/privilege/widget.viewer /// http://tizen.org/feature/shell.appwidget /// Thrown when the argument is null. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public string GetIconPath(string lang) { if (lang == null) throw new ArgumentNullException(); string pkgId = Interop.WidgetService.GetPkgId(Id); string str = Interop.WidgetService.GetIcon(pkgId, lang); Interop.WidgetService.ErrorCode err = (Interop.WidgetService.ErrorCode)Internals.Errors.ErrorFacts.GetLastResult(); switch (err) { case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); } return str; } /// /// Releases all the resources used by the WidgetControl class. /// /// 3 /// http://tizen.org/feature/shell.appwidget /// Thrown in case of failed conditions. /// Thrown when an application does not have the required privileges to access this method. /// Thrown when the required features are not supported. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { } _created = null; _resumed = null; _paused = null; _destroyed = null; UnregisterLifecycleEvent(); _disposedValue = true; } } private void RegisterLifecycleEvent() { if (!s_lifecycleEventRefCnt.ContainsKey(Id)) s_lifecycleEventRefCnt[Id] = 0; if (_created != null || _paused != null || _resumed != null || _destroyed != null) return; if (s_lifecycleEventRefCnt[Id] == 0) { if (_onLifecycleCallback == null) _onLifecycleCallback = new Interop.WidgetService.LifecycleCallback(OnLifecycleEvent); Interop.WidgetService.ErrorCode err = Interop.WidgetService.SetLifecycleEvent(Id, _onLifecycleCallback, IntPtr.Zero); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); } } s_lifecycleEventRefCnt[Id]++; s_eventObjects.Add(this); Log.Debug(LogTag, "register lifecycle cb " + Id + " [" + s_lifecycleEventRefCnt[Id] + "]"); } private void UnregisterLifecycleEvent() { if (!s_lifecycleEventRefCnt.ContainsKey(Id)) return; if (s_lifecycleEventRefCnt[Id] <= 0) return; if (_created != null || _paused != null || _resumed != null || _destroyed != null) return; if (s_lifecycleEventRefCnt[Id] == 1) { Interop.WidgetService.ErrorCode err = Interop.WidgetService.UnsetLifecycleEvent(Id, IntPtr.Zero); switch (err) { case Interop.WidgetService.ErrorCode.InvalidParameter: throw new InvalidOperationException("Invalid parameter at unmanaged code"); case Interop.WidgetService.ErrorCode.PermissionDenied: throw new UnauthorizedAccessException(); case Interop.WidgetService.ErrorCode.NotExist: throw new InvalidOperationException("Event handler is not exist"); case Interop.WidgetService.ErrorCode.NotSupported: throw new NotSupportedException(); } _onLifecycleCallback = null; } s_eventObjects.Remove(this); s_lifecycleEventRefCnt[Id]--; Log.Debug(LogTag, "unregister lifecycle cb " + Id + " [" + s_lifecycleEventRefCnt[Id] + "]"); } private static int OnLifecycleEvent(string widgetId, Interop.WidgetService.LifecycleEvent e, string instanceId, IntPtr userData) { Log.Debug(LogTag, "Lifecycle event : " + instanceId + " [" + e + "]"); switch (e) { case Interop.WidgetService.LifecycleEvent.Created: foreach (WidgetControl c in s_eventObjects) { if (c.Id.CompareTo(widgetId) == 0) { c._created?.Invoke(null, new WidgetLifecycleEventArgs() { WidgetId = widgetId, InstanceId = instanceId, Type = WidgetLifecycleEventArgs.EventType.Created }); } } break; case Interop.WidgetService.LifecycleEvent.Resumed: foreach (WidgetControl c in s_eventObjects) { if (c.Id.CompareTo(widgetId) == 0) { c._resumed?.Invoke(null, new WidgetLifecycleEventArgs() { WidgetId = widgetId, InstanceId = instanceId, Type = WidgetLifecycleEventArgs.EventType.Resumed }); } } break; case Interop.WidgetService.LifecycleEvent.Paused: foreach (WidgetControl c in s_eventObjects) { if (c.Id.CompareTo(widgetId) == 0) { c._paused?.Invoke(null, new WidgetLifecycleEventArgs() { WidgetId = widgetId, InstanceId = instanceId, Type = WidgetLifecycleEventArgs.EventType.Paused }); } } break; case Interop.WidgetService.LifecycleEvent.Destroyed: foreach (WidgetControl c in s_eventObjects) { if (c.Id.CompareTo(widgetId) == 0) { c._destroyed?.Invoke(null, new WidgetLifecycleEventArgs() { WidgetId = widgetId, InstanceId = instanceId, Type = WidgetLifecycleEventArgs.EventType.Destroyed }); } } break; } return 0; } } }