From: aman.jeph Date: Wed, 15 Sep 2021 08:46:06 +0000 (+0530) Subject: Adding screen-mirroring-sink package X-Git-Tag: submit/tizen/20210915.091636^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Ftizen_6.5;p=profile%2Fiot%2Fapps%2Fdotnet%2Fscreen-mirroring-sink.git Adding screen-mirroring-sink package Change-Id: I8e8acc9dc881e21a8c3d87f3403a818e2de2105f Signed-off-by: aman.jeph --- diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..54b213e --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/build.conf b/build.conf new file mode 100755 index 0000000..7831c55 --- /dev/null +++ b/build.conf @@ -0,0 +1,19 @@ +[general] +profile = profile.device + +[profile.device] + +obs = obs.tizen5.0 +user = obs_viewer +passwdx = obs_viewer +repos = repo.base_arm, repo.target_unified +buildroot = ~/GBS-ROOT + +[obs.tizen5.0] +url = https://api.tizen.org + +[repo.base_arm] +url = http://download.tizen.org/snapshots/tizen/base/latest/repos/standard/packages/ + +[repo.target_unified] +url = http://download.tizen.org/snapshots/tizen/unified/latest/repos/standard/packages/ diff --git a/packaging/org.tizen.ScreenMirroringSink-1.0.0.tpk b/packaging/org.tizen.ScreenMirroringSink-1.0.0.tpk new file mode 100755 index 0000000..6770634 Binary files /dev/null and b/packaging/org.tizen.ScreenMirroringSink-1.0.0.tpk differ diff --git a/packaging/org.tizen.ScreenMirroringSink.spec b/packaging/org.tizen.ScreenMirroringSink.spec new file mode 100755 index 0000000..38ff784 --- /dev/null +++ b/packaging/org.tizen.ScreenMirroringSink.spec @@ -0,0 +1,37 @@ +Name: org.tizen.ScreenMirroringSink +Summary: org.tizen.ScreenMirroringSink +Version: 1.0.0 +Release: 1 +Group: N/A +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz + +ExclusiveArch: i586 x86 i486 i686 i386 armv7l arm aarch64 + + +BuildRequires: pkgconfig(libtzplatform-config) +Requires(post): /usr/bin/tpk-backend + +%define internal_name org.tizen.ScreenMirroringSink +%define preload_tpk_path %{TZ_SYS_RO_APP}/.preload-tpk + +%description +profile/iot/apps/native/screen-mirroring +This is a container package which have preload TPK/WGT files + +%prep +%setup -q + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}/%{preload_tpk_path} +install packaging/%{internal_name}-%{version}.tpk %{buildroot}/%{preload_tpk_path}/ + +%post + +%files +%defattr(-,root,root,-) +%{preload_tpk_path}/* + diff --git a/screen-mirroring-sink/AppConstants.cs b/screen-mirroring-sink/AppConstants.cs new file mode 100755 index 0000000..6ab0a2d --- /dev/null +++ b/screen-mirroring-sink/AppConstants.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ScreenMirrorSink +{ + class AppConstants + { + // LogTag + public const string LogTag = "SCREEN_MIRRORING_SINK"; + + // KeyCodes + public const string BackKeyCode = "XF86Back"; + public const string EscapeKeyCode = "Escape"; + + // string literals + public const string LightPlatformThemeId = "org.tizen.default-light-theme"; + public const string DarkPlatformThemeId = "org.tizen.default-dark-theme"; + } +} diff --git a/screen-mirroring-sink/MirroringDisplay.cs b/screen-mirroring-sink/MirroringDisplay.cs new file mode 100755 index 0000000..d4d6779 --- /dev/null +++ b/screen-mirroring-sink/MirroringDisplay.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.Multimedia; +using Tizen.NUI; + +namespace ScreenMirrorSink +{ + class MirroringDisplay + { + public MirroringDisplay(Window window) + { + Display = new Display(window); + } + + private Display display; + + public Display Display + { + get => display; + private set => display = value; + } + } +} diff --git a/screen-mirroring-sink/MirroringManager.cs b/screen-mirroring-sink/MirroringManager.cs new file mode 100755 index 0000000..9637761 --- /dev/null +++ b/screen-mirroring-sink/MirroringManager.cs @@ -0,0 +1,127 @@ +using System; +using Tizen.Multimedia.Remoting; +using Tizen.NUI; + +namespace ScreenMirrorSink +{ + class MirroringManager + { + private WifiDirectController controller; + private MirroringDisplay mirroringDisplay; + private MirroringSink sink; + + public MirroringManager() + { + controller = new WifiDirectController(); + controller.DeviceConnected += OnDeviceConnected; + } + + private void OnDeviceConnected(object sender, EventArgs e) + { + Tizen.Log.Debug(AppConstants.LogTag, "device conneted"); + mirroringDisplay = new MirroringDisplay(Window.Instance); + sink = new MirroringSink(); + sink.SourceConnected += OnSourceConnected; + StartMirroring(); + } + + private void Start() + { + if (sink.CurrentState == ScreenMirroringState.Connected) + { + sink.Start(); + } + else + { + Tizen.Log.Error(AppConstants.LogTag, "Sink is not in connected state, Current State: " + sink.CurrentState.ToString()); + } + } + + private void StartMirroring() + { + SetUpConnection(); + } + + public void Pause() + { + if(sink?.CurrentState == ScreenMirroringState.Playing) + { + sink.Pause(); + } + else + { + Tizen.Log.Error(AppConstants.LogTag, "Sink is not in playing state, Current State: " + sink?.CurrentState.ToString()); + } + } + + public void Resume() + { + if(sink?.CurrentState == ScreenMirroringState.Paused) + { + sink.Resume(); + } + } + + public void Stop() + { + if(sink.CurrentState == ScreenMirroringState.Connected || sink.CurrentState == ScreenMirroringState.Playing || sink.CurrentState == ScreenMirroringState.Paused) + { + Tizen.Log.Debug(AppConstants.LogTag, "Disconnecting sink"); + sink.Disconnect(); + } + + if(sink.CurrentState == ScreenMirroringState.Disconnected || sink.CurrentState == ScreenMirroringState.Prepared) + { + Tizen.Log.Debug(AppConstants.LogTag, "Unpreparing sink"); + sink.Unprepare(); + } + } + + public void Terminate() + { + Stop(); + controller.StopWifiDirect(); + } + + private void OnSourceConnected(object sender, EventArgs e) + { + Tizen.Log.Info(AppConstants.LogTag, "Screen Mirroring source connected, now start screen mirroring"); + PermissionPopup permissionPopup = new PermissionPopup(controller.ConnectedDeviceName); + permissionPopup.PermissionRequest += OnPermissionRequest; + } + + private void OnPermissionRequest(object sender, PermissionEventHandlerArgs e) + { + ((PermissionPopup)sender).Dispose(); + if (e.Permission == RequestPermission.Allow) + { + Start(); + } + else if(e.Permission == RequestPermission.Deny) + { + Tizen.Applications.Application.Current.Exit(); + } + } + + private void SetUpConnection() + { + if(sink.CurrentState == ScreenMirroringState.Connected) + { + Tizen.Log.Info(AppConstants.LogTag, "Sink is already in connected state"); + return; + } + + if(sink.CurrentState == ScreenMirroringState.Idle) + { + Tizen.Log.Debug(AppConstants.LogTag, "Preparing Sink"); + sink.Prepare(mirroringDisplay.Display); + } + if(sink.CurrentState == ScreenMirroringState.Prepared) + { + Tizen.Log.Debug(AppConstants.LogTag, "Connecting to Source"); + Tizen.Log.Debug(AppConstants.LogTag, "Source Ip: " + controller.ConnectedDeviceIp+":"+controller.ConnectedDevicePort+ ", Name: " + controller.ConnectedDeviceName); + sink.Connect(controller.ConnectedDeviceIp, (uint)controller.ConnectedDevicePort); + } + } + } +} diff --git a/screen-mirroring-sink/MirroringSink.cs b/screen-mirroring-sink/MirroringSink.cs new file mode 100755 index 0000000..c4504a2 --- /dev/null +++ b/screen-mirroring-sink/MirroringSink.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.Multimedia.Remoting; +using Tizen.Multimedia; + +namespace ScreenMirrorSink +{ + class MirroringSink + { + private readonly ScreenMirroring screenMirroring = new ScreenMirroring(); + + public event EventHandler SourceConnected; + + public MirroringSink() + { + currentState = ScreenMirroringState.Idle; + screenMirroring.StateChanged += OnScreenMirroringStateChange; + screenMirroring.ErrorOccurred += OnScreenMirroringErrorOccured; + } + + private void OnScreenMirroringErrorOccured(object sender, ScreenMirroringErrorOccurredEventArgs e) + { + Tizen.Log.Error(AppConstants.LogTag, "Screen Mirroring error occurred: " + e.Error.ToString()); + } + + private void OnScreenMirroringStateChange(object sender, ScreenMirroringStateChangedEventArgs e) + { + Tizen.Log.Debug(AppConstants.LogTag, "Screen Mirror State changed: " + e.State.ToString()); + currentState = e.State; + if (e.State == ScreenMirroringState.Connected) + { + SourceConnected?.Invoke(this, new EventArgs()); + } + } + + private ScreenMirroringState currentState; + + public ScreenMirroringState CurrentState + { + get => currentState; + } + + public void Prepare(Display display) + { + try + { + screenMirroring.Prepare(display, ScreenMirroringResolutions.R640x360P30); + } + catch (ArgumentNullException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Argument null: " + ex.ParamName); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + catch (ArgumentException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Argument exception: " + ex.ParamName + ", " + ex.Message); + } + } + + public async void Connect(string sourceIp, uint port) + { + try + { + await screenMirroring.ConnectAsync(sourceIp, port); + } + catch(UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch(ArgumentNullException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Argument null: " + ex.ParamName); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + catch(ArgumentException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Argument exception: " + ex.ParamName+", "+ex.Message); + } + } + + public async void Start() + { + try + { + await screenMirroring.StartAsync(); + } + catch (UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + } + + public async void Pause() + { + try + { + await screenMirroring.PauseAsync(); + } + catch (UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + } + + public async void Resume() + { + try + { + await screenMirroring.ResumeAsync(); + } + catch (UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + } + + public void Disconnect() + { + try + { + screenMirroring.Disconnect(); + } + catch (UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + } + + public void Unprepare() + { + try + { + screenMirroring.Unprepare(); + } + catch (ObjectDisposedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Object disposed : " + ex.Message); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + } + } +} diff --git a/screen-mirroring-sink/PermissionEventHandlerArgs.cs b/screen-mirroring-sink/PermissionEventHandlerArgs.cs new file mode 100755 index 0000000..1489ee7 --- /dev/null +++ b/screen-mirroring-sink/PermissionEventHandlerArgs.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ScreenMirrorSink +{ + + public enum RequestPermission + { + Allow, + Deny + }; + class PermissionEventHandlerArgs : EventArgs + { + internal PermissionEventHandlerArgs(RequestPermission requestPermission) + { + Permission = requestPermission; + } + + public RequestPermission Permission { get; private set; } + } +} diff --git a/screen-mirroring-sink/PermissionPopup.cs b/screen-mirroring-sink/PermissionPopup.cs new file mode 100755 index 0000000..078ff43 --- /dev/null +++ b/screen-mirroring-sink/PermissionPopup.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using Tizen.NUI; +using Tizen.NUI.Components; +using Tizen.NUI.BaseComponents; + +namespace ScreenMirrorSink +{ + class PermissionPopup : IDisposable + { + private AlertDialog alertDialog; + private TextLabel titleLabel; + private TextLabel contentLabel; + private Button cancelbutton; + private Button allowButton; + private bool disposedValue; + + public event EventHandler PermissionRequest; + public PermissionPopup(string name) + { + titleLabel = new TextLabel("LabelText") + { + ThemeChangeSensitive = true, + PixelSize = 40, + FontFamily = "BreezeSans", + FontStyle = UIConstants.AllNormal, + Text = "Screen Mirroring", + }; + + View contentArea = new View() + { + SizeHeight = 144, + WidthResizePolicy = ResizePolicyType.FillToParent, + BackgroundColor = Color.Transparent, + }; + + contentLabel = new TextLabel("LabelText") + { + ThemeChangeSensitive = true, + PixelSize = 32, + FontFamily = "BreezeSans", + FontStyle = UIConstants.NormalLight, + Text = "Allow [" + name + "] to mirror on this device", + WidthResizePolicy = ResizePolicyType.FillToParent, + HorizontalAlignment = HorizontalAlignment.Begin, + PositionY = 40, + }; + contentArea.Add(contentLabel); + + cancelbutton = new Button("CancelButton") + { + ThemeChangeSensitive = true, + }; + cancelbutton.TextLabel.FontStyle = UIConstants.AllNormal; + cancelbutton.Clicked += OnCanelClicked; + + allowButton = new Button() + { + Text = "Allow", + }; + allowButton.Clicked += OnAllowClicked; + + alertDialog = new AlertDialog() + { + TitleContent = titleLabel, + Content = contentArea, + Actions = new List { cancelbutton, allowButton }, + }; + Window.Instance.Add(alertDialog); + } + + private void OnAllowClicked(object sender, ClickedEventArgs e) + { + PermissionRequest?.Invoke(this, new PermissionEventHandlerArgs(RequestPermission.Allow)); + } + + private void OnCanelClicked(object sender, ClickedEventArgs e) + { + PermissionRequest?.Invoke(this, new PermissionEventHandlerArgs(RequestPermission.Deny)); + } + + private void RemoveAlertDialog() + { + Window.Instance.Remove(alertDialog); + alertDialog.Dispose(); + alertDialog = null; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + RemoveAlertDialog(); + disposedValue = true; + } + } + + // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~PermissionPopup() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/screen-mirroring-sink/Resources.cs b/screen-mirroring-sink/Resources.cs new file mode 100755 index 0000000..dc9d17f --- /dev/null +++ b/screen-mirroring-sink/Resources.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ScreenMirrorSink +{ + public class Resources + { + public static string GetImagePath() + { + return Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/"; + } + + public static string GetThemePath() + { + return Tizen.Applications.Application.Current.DirectoryInfo.Resource + "themes/"; + } + } +} diff --git a/screen-mirroring-sink/ScreenMirroringSink.cs b/screen-mirroring-sink/ScreenMirroringSink.cs new file mode 100755 index 0000000..bbe59be --- /dev/null +++ b/screen-mirroring-sink/ScreenMirroringSink.cs @@ -0,0 +1,123 @@ +using System; +using System.IO; +using Tizen.NUI; +using Tizen.NUI.Xaml; + +namespace ScreenMirrorSink +{ + class Application : NUIApplication + { + private MirroringManager manager; + + public Application() : base(ThemeOptions.PlatformThemeEnabled) + { + + } + + protected override void OnCreate() + { + base.OnCreate(); + Initialize(); + } + + protected override void OnPause() + { + base.OnPause(); + manager?.Pause(); + } + + protected override void OnResume() + { + base.OnResume(); + manager?.Resume(); + } + + protected override void OnTerminate() + { + base.OnTerminate(); + manager?.Terminate(); + } + + void Initialize() + { + Window.Instance.BackgroundColor = Color.Transparent; + Window.Instance.KeyEvent += OnKeyEvent; + + UpdateTheme(ThemeManager.PlatformThemeId); + RegisterThemeChanges(); + + TitleBarView titleBarView = new TitleBarView(); + Window.Instance.GetDefaultLayer().Add(titleBarView); + + manager = new MirroringManager(); + } + + public void OnKeyEvent(object sender, Window.KeyEventArgs e) + { + if (e.Key.State == Key.StateType.Down && (e.Key.KeyPressedName == AppConstants.BackKeyCode || e.Key.KeyPressedName == AppConstants.EscapeKeyCode)) + { + Exit(); + } + } + + private void RegisterThemeChanges() + { + ThemeManager.ThemeChanged += (object sender, ThemeChangedEventArgs e) => + { + if (e.IsPlatformThemeChanged) + { + Tizen.Log.Error(AppConstants.LogTag, "Theme Changed: " + e.ThemeId); + UpdateTheme(e.PlatformThemeId); + } + }; + } + + private void SetTheme(string path) + { + try + { + Theme lightTheme = new Theme(path); + ThemeManager.ApplyTheme(lightTheme); + } + catch (ArgumentNullException e) + { + Tizen.Log.Error(AppConstants.LogTag, "ArgumentNullException: " + e.ParamName); + } + catch (IOException e) + { + Tizen.Log.Error(AppConstants.LogTag, "IOException: " + e.Message); + } + catch (XamlParseException e) + { + Tizen.Log.Error(AppConstants.LogTag, "XamlParseException: " + e.Message); + if (e.XmlInfo != null) + { + Tizen.Log.Error(AppConstants.LogTag, "XamlParseException, LineNo." + e.XmlInfo.LineNumber + " Pos: " + e.XmlInfo.LinePosition + " HasInfo: " + e.XmlInfo.HasLineInfo().ToString()); + } + } + } + + private void UpdateTheme(string platformThemeId) + { + if (platformThemeId == null) + { + Tizen.Log.Error(AppConstants.LogTag, "Platform theme id is null"); + return; + } + if (platformThemeId.Equals(AppConstants.LightPlatformThemeId)) + { + SetTheme(Resources.GetThemePath() + "light.xaml"); + } + else if (platformThemeId.Equals(AppConstants.DarkPlatformThemeId)) + { + SetTheme(Resources.GetThemePath() + "dark.xaml"); + } + } + + static void Main(string[] args) + { + var app = new Application(); + app.Run(args); + } + } +} diff --git a/screen-mirroring-sink/ScreenMirroringSink.csproj b/screen-mirroring-sink/ScreenMirroringSink.csproj new file mode 100755 index 0000000..5ee4675 --- /dev/null +++ b/screen-mirroring-sink/ScreenMirroringSink.csproj @@ -0,0 +1,28 @@ + + + + Exe + netcoreapp3.1 + Tizen + ScreenMirroringSink + + + + portable + + + None + + + + + + + + + + + + + + diff --git a/screen-mirroring-sink/ScreenMirroringSink.sln b/screen-mirroring-sink/ScreenMirroringSink.sln new file mode 100755 index 0000000..0153f0d --- /dev/null +++ b/screen-mirroring-sink/ScreenMirroringSink.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31025.194 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenMirroringSink", "ScreenMirroringSink.csproj", "{C3A5CC64-F899-4116-9959-B45B30D63A29}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C3A5CC64-F899-4116-9959-B45B30D63A29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3A5CC64-F899-4116-9959-B45B30D63A29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3A5CC64-F899-4116-9959-B45B30D63A29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3A5CC64-F899-4116-9959-B45B30D63A29}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CF208F67-5E2E-4605-AE1B-5C6DDAA0593B} + EndGlobalSection +EndGlobal diff --git a/screen-mirroring-sink/TitleBarView.cs b/screen-mirroring-sink/TitleBarView.cs new file mode 100755 index 0000000..12037d2 --- /dev/null +++ b/screen-mirroring-sink/TitleBarView.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; + +namespace ScreenMirrorSink +{ + class TitleBarView : View + { + private const int Interval = 3000; + private const int AnimationTime = 300; + private const int ShowAnimationPosition = 0; + private const int HideAnimationPosition = -120; + + private View titleBar; + private Button backButton; + private TextLabel titleLabel; + private Timer autoHideTimer; + private Animation showHideAnimation; + private bool isAnimating; + private bool isTitleBarVisible; + public TitleBarView() : base() + { + Name = "TitleBarView"; + //StyleName = "AppBackground"; + BackgroundColor = Color.Transparent; + ThemeChangeSensitive = true; + WidthResizePolicy = ResizePolicyType.FillToParent; + HeightResizePolicy = ResizePolicyType.FillToParent; + titleBar = new View() + { + BackgroundColor = Color.Transparent, + WidthResizePolicy = ResizePolicyType.FillToParent, + SizeHeight = 120, + Layout = new RelativeLayout() + { + Padding = new Extents(64, 64, 0, 0), + }, + PositionX = 0, + PositionY = -120, + }; + Add(titleBar); + + backButton = new Button("BackButton") + { + ThemeChangeSensitive = true, + }; + backButton.Clicked += (object sender, ClickedEventArgs e) => + { + Tizen.Applications.Application.Current.Exit(); + }; + titleBar.Add(backButton); + titleBar.Hide(); + isTitleBarVisible = false; + RelativeLayout.SetLeftRelativeOffset(backButton, 0.0f); + RelativeLayout.SetRightRelativeOffset(backButton, 0.0f); + RelativeLayout.SetHorizontalAlignment(backButton, RelativeLayout.Alignment.Start); + RelativeLayout.SetVerticalAlignment(backButton, RelativeLayout.Alignment.Center); + + titleLabel = new TextLabel("LabelText") + { + Text = "Screen Mirroring", + PixelSize = 40, + FontFamily = "BreezeSans", + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Center, + Ellipsis = true, + FontStyle = UIConstants.NormalLight, + Margin = new Extents(24, 0, 0, 0), + }; + titleBar.Add(titleLabel); + RelativeLayout.SetLeftTarget(titleLabel, backButton); + RelativeLayout.SetLeftRelativeOffset(titleLabel, 1.0f); + RelativeLayout.SetRightTarget(titleLabel, titleBar); + RelativeLayout.SetRightRelativeOffset(titleLabel, 1.0f); + RelativeLayout.SetFillHorizontal(titleLabel, true); + RelativeLayout.SetVerticalAlignment(titleLabel, RelativeLayout.Alignment.Center); + + showHideAnimation = new Animation(AnimationTime); + showHideAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear); + showHideAnimation.Finished += OnAnimationFinished; + isAnimating = false; + + autoHideTimer = new Timer(Interval); + autoHideTimer.Tick += OnAutoHide; + + TouchEvent += OnTitleBarViewTouch; + } + + private bool OnTitleBarViewTouch(object source, TouchEventArgs e) + { + PointStateType pointState = e.Touch.GetState(0); + if (pointState == PointStateType.Up) + { + if (isAnimating) + { + return false; + } + + if (isTitleBarVisible) + { + HideTitleBar(); + } + else + { + ShowTitleBar(); + } + } + + return false; + } + + private void OnAnimationFinished(object sender, EventArgs e) + { + isAnimating = false; + if(isTitleBarVisible == false) + { + titleBar.Hide(); + } + } + + public void ShowTitleBar() + { + titleBar.Show(); + isTitleBarVisible = true; + AnimateTitleBar(true); + autoHideTimer.Start(); + } + + public void HideTitleBar() + { + autoHideTimer.Stop(); + isTitleBarVisible = false; + AnimateTitleBar(false); + } + + private bool OnAutoHide(object source, Timer.TickEventArgs e) + { + HideTitleBar(); + return false; + } + + private void AnimateTitleBar(bool show) + { + int destionValue = show ? ShowAnimationPosition : HideAnimationPosition; + showHideAnimation.AnimateTo(titleBar, "PositionY", destionValue); + showHideAnimation.Play(); + isAnimating = true; + } + + protected override void Dispose(DisposeTypes type) + { + if(Disposed) + { + return; + } + + if(type == DisposeTypes.Explicit) + { + titleBar.Remove(titleLabel); + titleBar.Remove(backButton); + + titleLabel.Dispose(); + titleLabel = null; + + backButton.Dispose(); + backButton = null; + + base.Remove(titleBar); + titleBar.Dispose(); + titleBar = null; + + autoHideTimer.Dispose(); + autoHideTimer = null; + + showHideAnimation.Finished -= OnAnimationFinished; + showHideAnimation.Dispose(); + showHideAnimation = null; + + TouchEvent -= OnTitleBarViewTouch; + } + + base.Dispose(type); + } + + } +} diff --git a/screen-mirroring-sink/UIConstants.cs b/screen-mirroring-sink/UIConstants.cs new file mode 100755 index 0000000..5e49677 --- /dev/null +++ b/screen-mirroring-sink/UIConstants.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.NUI; + +namespace ScreenMirrorSink +{ + class UIConstants + { + public static readonly Color HEXEEEFF1 = new Color(0.9333f, 0.9373f, 0.9450f, 1.0f); + public static readonly PropertyMap NormalLight = new PropertyMap(). + Add("width", new PropertyValue("normal")). + Add("weight", new PropertyValue("light")); + + public static readonly PropertyMap AllNormal = new PropertyMap(). + Add("width", new PropertyValue("normal")). + Add("weight", new PropertyValue("normal")). + Add("slant", new PropertyValue("normal")); + } +} diff --git a/screen-mirroring-sink/WifiDirectController.cs b/screen-mirroring-sink/WifiDirectController.cs new file mode 100755 index 0000000..162ae35 --- /dev/null +++ b/screen-mirroring-sink/WifiDirectController.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.Network.WiFiDirect; + +namespace ScreenMirrorSink +{ + class WifiDirectController + { + + public event EventHandler DeviceConnected; + + private struct PeerInfo + { + public PeerInfo(WiFiDirectPeer peer ) + { + Name = peer.Name; + IpAddress = peer.IpAddress; + MacAddress = peer.MacAddress; + Port = peer.Port; + } + + public string Name { get; private set; } + public string IpAddress { get; private set; } + public string MacAddress { get; private set; } + public int Port { get; private set; } + } + + private List peerInfoList; + + public WifiDirectController() + { + WiFiDirectManager.DeviceStateChanged += OnDeviceStatusChanged; + WiFiDirectManager.ConnectionStatusChanged += OnConnectionStateChanged; + WiFiDirectManager.DiscoveryStateChanged += OnDiscoveryStateChanged; + WiFiDirectManager.StateChanged += OnStateChanged; + WiFiDirectManager.PeerFound += OnPeerFound; + WiFiDirectManager.Activate(); + peerInfoList = new List(); + } + + public string ConnectedDeviceName + { + get + { + if (peerInfoList.Count > 0) + { + return peerInfoList[0].Name; + } + return string.Empty; + } + } + + public string ConnectedDeviceIp + { + get + { + if(peerInfoList.Count > 0) + { + return peerInfoList[0].IpAddress; + } + return string.Empty; + } + } + + public string ConnectedDeviceMac + { + get + { + if (peerInfoList.Count > 0) + { + return peerInfoList[0].MacAddress; + } + return string.Empty; + } + } + + public int ConnectedDevicePort + { + get + { + if (peerInfoList.Count > 0) + { + return peerInfoList[0].Port; + } + return -1; + } + } + + public void StopWifiDirect() + { + WiFiDirectManager.DeinitDisplay(); + if (WiFiDirectManager.IsDiscoverable) + { + WiFiDirectManager.CancelDiscovery(); + } + WiFiDirectManager.Deactivate(); + } + + private void OnPeerFound(object sender, PeerFoundEventArgs e) + { + Tizen.Log.Error(AppConstants.LogTag, "Peer Found Error: " + e.Error.ToString()); + Tizen.Log.Error(AppConstants.LogTag, "DiscoveryState: " + e.DiscoveryState.ToString()); + if(e.Error == WiFiDirectError.None) + { + Tizen.Log.Info(AppConstants.LogTag, e.Peer.Name+", "+e.Peer.IpAddress+", "+e.Peer.MacAddress+", "+e.Peer.IsConnected.ToString()); + } + } + + private void OnStateChanged(object sender, StateChangedEventArgs e) + { + Tizen.Log.Error(AppConstants.LogTag, "Current State: " + e.State.ToString()); + if(e.State == WiFiDirectState.Connected) + { + IEnumerable wiFiDirectPeers = WiFiDirectManager.GetConnectedPeers(); + peerInfoList.Clear(); + foreach(WiFiDirectPeer peer in wiFiDirectPeers) + { + peerInfoList.Add(new PeerInfo(peer)); + } + Tizen.Log.Info(AppConstants.LogTag, "Connected peer count: " + peerInfoList.Count); + DeviceConnected?.Invoke(this, new EventArgs()); + } + } + + private void OnDiscoveryStateChanged(object sender, DiscoveryStateChangedEventArgs e) + { + Tizen.Log.Debug(AppConstants.LogTag, "Discovery Error: " + e.Error.ToString()); + Tizen.Log.Debug(AppConstants.LogTag, "DiscoveryState: " + e.DiscoveryState.ToString()); + } + + private void OnConnectionStateChanged(object sender, ConnectionStatusChangedEventArgs e) + { + Tizen.Log.Debug(AppConstants.LogTag, "Connection Error: " + e.Error.ToString()); + Tizen.Log.Debug(AppConstants.LogTag, "Connection State: " + e.ConnectionState.ToString()); + } + + private void OnDeviceStatusChanged(object sender, DeviceStateChangedEventArgs e) + { + Tizen.Log.Debug(AppConstants.LogTag, "DeviceState Error: " + e.Error); + Tizen.Log.Debug(AppConstants.LogTag, "DeviceState: " + e.DeviceState.ToString()); + if(e.DeviceState == WiFiDirectDeviceState.Activated) + { + Tizen.Log.Info(AppConstants.LogTag, "Start setting up wifi direct display"); + SetupWifiDirectDisplay(); + } + } + + private void SetupWifiDirectDisplay() + { + try + { + //WiFiDirectManager.DisconnectAll(); + WiFiDirectManager.InitDisplay(); + WiFiDirectManager.SetDisplayAvailability(true); + WiFiDirectManager.SetDisplay(WiFiDirectDisplayType.Prisink, 2022, 0); + WiFiDirectManager.GroupOwnerIntent = 1; + WiFiDirectManager.MaxClients = 1; + WiFiDirectManager.StartDiscovery(false, 100); + } + catch (InvalidOperationException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Invalid operation null: " + ex.Message); + } + catch (UnauthorizedAccessException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Unauthorized access: " + ex.Message); + } + catch(NotSupportedException ex) + { + Tizen.Log.Error(AppConstants.LogTag, "Not supported access: " + ex.Message); + } + } + } +} diff --git a/screen-mirroring-sink/WifiDirectPeerController.cs b/screen-mirroring-sink/WifiDirectPeerController.cs new file mode 100755 index 0000000..d3b93e0 --- /dev/null +++ b/screen-mirroring-sink/WifiDirectPeerController.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.Network.WiFiDirect; + +namespace ScreenMirrorSink +{ + class WifiDirectPeerController + { + private WiFiDirectPeer currentPeer; + + public WifiDirectPeerController(WiFiDirectPeer peer) + { + if(peer == null) + { + throw new ArgumentNullException(nameof(peer), "provided peer is null"); + } + currentPeer = peer; + } + + public string Name + { + get => currentPeer.Name; + } + + public string IpAddress + { + get => currentPeer.IpAddress; + } + + public WiFiDirectDisplayType DisplayType + { + get => currentPeer.Display; + } + } +} diff --git a/screen-mirroring-sink/res/images/dark/back.png b/screen-mirroring-sink/res/images/dark/back.png new file mode 100755 index 0000000..b6e3472 Binary files /dev/null and b/screen-mirroring-sink/res/images/dark/back.png differ diff --git a/screen-mirroring-sink/res/images/dark/cancel_button_bg.png b/screen-mirroring-sink/res/images/dark/cancel_button_bg.png new file mode 100755 index 0000000..3c4f884 Binary files /dev/null and b/screen-mirroring-sink/res/images/dark/cancel_button_bg.png differ diff --git a/screen-mirroring-sink/res/images/light/back.png b/screen-mirroring-sink/res/images/light/back.png new file mode 100755 index 0000000..e44b1a2 Binary files /dev/null and b/screen-mirroring-sink/res/images/light/back.png differ diff --git a/screen-mirroring-sink/res/images/light/cancel_button_bg.png b/screen-mirroring-sink/res/images/light/cancel_button_bg.png new file mode 100755 index 0000000..c867370 Binary files /dev/null and b/screen-mirroring-sink/res/images/light/cancel_button_bg.png differ diff --git a/screen-mirroring-sink/res/themes/dark.xaml b/screen-mirroring-sink/res/themes/dark.xaml new file mode 100755 index 0000000..04b2935 --- /dev/null +++ b/screen-mirroring-sink/res/themes/dark.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + *Resource*/images/dark/cancel_button_bg.png + + + + + + + + \ No newline at end of file diff --git a/screen-mirroring-sink/res/themes/light.xaml b/screen-mirroring-sink/res/themes/light.xaml new file mode 100755 index 0000000..efb16f2 --- /dev/null +++ b/screen-mirroring-sink/res/themes/light.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + *Resource*/images/light/cancel_button_bg.png + + + + + + + + \ No newline at end of file diff --git a/screen-mirroring-sink/shared/res/ScreenMirroringSink.png b/screen-mirroring-sink/shared/res/ScreenMirroringSink.png new file mode 100755 index 0000000..9f3cb98 Binary files /dev/null and b/screen-mirroring-sink/shared/res/ScreenMirroringSink.png differ diff --git a/screen-mirroring-sink/tizen-manifest.xml b/screen-mirroring-sink/tizen-manifest.xml new file mode 100755 index 0000000..c1cfb6e --- /dev/null +++ b/screen-mirroring-sink/tizen-manifest.xml @@ -0,0 +1,19 @@ + + + + + + ScreenMirroringSink.png + + + + + + http://tizen.org/privilege/internet + http://tizen.org/privilege/wifidirect + + + + + +