diff --git a/IoT/UI/AppCommon/AppCommon.png b/IoT/UI/AppCommon/AppCommon.png new file mode 100755 index 000000000..a38504b74 Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon.png differ diff --git a/IoT/UI/AppCommon/AppCommon.sln b/IoT/UI/AppCommon/AppCommon.sln new file mode 100755 index 000000000..d8af4e2df --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppCommon", "AppCommon\AppCommon.csproj", "{E4FE2ADD-EAF4-4152-BC2F-3DFF44C2443E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E4FE2ADD-EAF4-4152-BC2F-3DFF44C2443E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4FE2ADD-EAF4-4152-BC2F-3DFF44C2443E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4FE2ADD-EAF4-4152-BC2F-3DFF44C2443E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4FE2ADD-EAF4-4152-BC2F-3DFF44C2443E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DC210600-66CB-4C43-94CE-4713D9FA403A} + EndGlobalSection +EndGlobal diff --git a/IoT/UI/AppCommon/AppCommon/AppCommon.cs b/IoT/UI/AppCommon/AppCommon/AppCommon.cs new file mode 100755 index 000000000..30014e5d2 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/AppCommon.cs @@ -0,0 +1,112 @@ +using AppCommon.Services; +using System; +using Tizen.Applications; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using AppCommon.Models; +using AppCommon.Pages; +using AppCommon.Services; + +namespace AppCommon +{ + class Program : NUIApplication + { + private Window window; + private Navigator navigator; + private MainPage mainPage; + + protected override void OnCreate() + { + base.OnCreate(); + Initialize(); + } + + void Initialize() + { + // Set IsUsingXaml to false as per migration requirements + NUIApplication.IsUsingXaml = false; + + // Initialize service locator + ServiceLocator.Initialize(); + + window = NUIApplication.GetDefaultWindow(); + window.BackgroundColor = Color.White; + + // Get window size + var windowSize = window.WindowSize; + int screenWidth = (int)windowSize.Width; + int screenHeight = (int)windowSize.Height; + + // Get default navigator for page-based navigation + navigator = window.GetDefaultNavigator(); + + // Push main page with tabs + mainPage = new MainPage(screenWidth, screenHeight); + navigator.Push(mainPage); + } + + /// + /// Handle LowMemory event + /// + /// Low memory event arguments + protected override void OnLowMemory(LowMemoryEventArgs e) + { + base.OnLowMemory(e); + + // Convert Tizen.Applications.LowMemoryStatus to our LowMemoryStatus enum + var status = Models.LowMemoryStatus.None; + switch (e.LowMemoryStatus) + { + case Tizen.Applications.LowMemoryStatus.Normal: + status = Models.LowMemoryStatus.Normal; + break; + case Tizen.Applications.LowMemoryStatus.SoftWarning: + status = Models.LowMemoryStatus.SoftWarning; + break; + case Tizen.Applications.LowMemoryStatus.HardWarning: + status = Models.LowMemoryStatus.HardWarning; + break; + } + + // Update ViewModel + mainPage?.AppInfoPage?.ViewModel?.UpdateLowMemoryLEDColor(status); + } + + /// + /// Handle DeviceOrientation event + /// + /// Device orientation event arguments + protected override void OnDeviceOrientationChanged(DeviceOrientationEventArgs e) + { + base.OnDeviceOrientationChanged(e); + + // Convert Tizen.Applications.DeviceOrientation to our DeviceOrientationStatus enum + var orientation = Models.DeviceOrientationStatus.Orientation_0; + switch (e.DeviceOrientation) + { + case Tizen.Applications.DeviceOrientation.Orientation_0: + orientation = Models.DeviceOrientationStatus.Orientation_0; + break; + case Tizen.Applications.DeviceOrientation.Orientation_90: + orientation = Models.DeviceOrientationStatus.Orientation_90; + break; + case Tizen.Applications.DeviceOrientation.Orientation_180: + orientation = Models.DeviceOrientationStatus.Orientation_180; + break; + case Tizen.Applications.DeviceOrientation.Orientation_270: + orientation = Models.DeviceOrientationStatus.Orientation_270; + break; + } + + // Update ViewModel + mainPage?.AppInfoPage?.ViewModel?.UpdateDeviceOrientation(orientation); + } + + static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/AppCommon.csproj b/IoT/UI/AppCommon/AppCommon/AppCommon.csproj new file mode 100755 index 000000000..8ecc55db0 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/AppCommon.csproj @@ -0,0 +1,23 @@ + + + + Exe + net6.0-tizen + + + + portable + + + None + + + + + + + + + + + diff --git a/IoT/UI/AppCommon/AppCommon/Models/Enums.cs b/IoT/UI/AppCommon/AppCommon/Models/Enums.cs new file mode 100755 index 000000000..eaf82ae38 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Models/Enums.cs @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +namespace AppCommon.Models +{ + /// + /// Low battery status enumeration + /// + public enum LowBatteryStatus + { + None, + PowerOff, + CriticalLow + } + + /// + /// Low memory status enumeration + /// + public enum LowMemoryStatus + { + None, + Normal, + SoftWarning, + HardWarning + } + + /// + /// Device orientation status enumeration + /// + public enum DeviceOrientationStatus + { + Orientation_0, + Orientation_90, + Orientation_180, + Orientation_270 + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Models/PathInformation.cs b/IoT/UI/AppCommon/AppCommon/Models/PathInformation.cs new file mode 100755 index 000000000..da94f1c7e --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Models/PathInformation.cs @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +namespace AppCommon.Models +{ + /// + /// A class to define each path information + /// + public class PathInformation + { + public string Title { get; set; } + public string Path { get; set; } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Pages/ApplicationInformationPage.cs b/IoT/UI/AppCommon/AppCommon/Pages/ApplicationInformationPage.cs new file mode 100755 index 000000000..e24c61171 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Pages/ApplicationInformationPage.cs @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2025 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.ComponentModel; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using AppCommon.ViewModels; + +namespace AppCommon.Pages +{ + /// + /// Application information page + /// + public class ApplicationInformationPage : ContentPage + { + private ApplicationInformationViewModel _viewModel; + + // UI Elements + private ImageView _icon; + private TextLabel _applicationName; + private TextLabel _applicationID; + private TextLabel _applicationVersion; + private ImageView _memoryLED; + private ImageView _batteryLED; + private TextLabel _language; + private TextLabel _regionFormat; + private TextLabel _deviceOrientation; + private TextLabel _rotationDegree; + + private int _screenWidth; + private int _screenHeight; + private bool _isLandscape; + + public ApplicationInformationViewModel ViewModel => _viewModel; + + public ApplicationInformationPage(int screenWidth = 720, int screenHeight = 1280) + { + _screenWidth = screenWidth; + _screenHeight = screenHeight; + _isLandscape = screenWidth > screenHeight; + + // Create ViewModel + _viewModel = new ApplicationInformationViewModel(); + + InitializeComponent(); + SetupDataBinding(); + } + + private void InitializeComponent() + { + // Set page properties + AppBar = new AppBar + { + Title = "Application Information" + }; + + // Create main layout with simple background + var mainLayout = new View + { + Layout = new AbsoluteLayout(), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + BackgroundColor = new Color(0.95f, 0.95f, 0.95f, 1.0f) // Light gray background + }; + + // Adaptive layout calculations + float iconSize = _isLandscape ? 100f : 150f; + float margin = _isLandscape ? 20f : 30f; + float topOffset = _isLandscape ? 20f : 80f; + float ledSize = _isLandscape ? 15f : 20f; + + // Application Icon + _icon = new ImageView + { + Size = new Size(iconSize, iconSize), + Position = new Position(margin, topOffset) + }; + mainLayout.Add(_icon); + + // Application Name Label + var nameLabel = new TextLabel + { + Text = "Name:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(margin, topOffset + iconSize + 15) + }; + mainLayout.Add(nameLabel); + + // Application Name + _applicationName = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size(_screenWidth - margin * 2, 40), + Position = new Position(margin, topOffset + iconSize + 35) + }; + mainLayout.Add(_applicationName); + + // Application ID Label + var idLabel = new TextLabel + { + Text = "ID:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(margin, topOffset + iconSize + 80) + }; + mainLayout.Add(idLabel); + + // Application ID + _applicationID = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size((_screenWidth - margin * 3) / 2, 50), + Position = new Position(margin, topOffset + iconSize + 100) + }; + mainLayout.Add(_applicationID); + + // Application Version Label + var versionLabel = new TextLabel + { + Text = "Version:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(_screenWidth / 2 + margin / 2, topOffset + iconSize + 80) + }; + mainLayout.Add(versionLabel); + + // Application Version + _applicationVersion = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size((_screenWidth - margin * 3) / 2, 40), + Position = new Position(_screenWidth / 2 + margin / 2, topOffset + iconSize + 100) + }; + mainLayout.Add(_applicationVersion); + + // Memory LED (simple colored circle) + float ledYPos = topOffset + iconSize + 170; + _memoryLED = new ImageView + { + Size = new Size(ledSize, ledSize), + Position = new Position(margin, ledYPos), + BackgroundColor = Resources.LightGray, + CornerRadius = ledSize / 2.0f + }; + mainLayout.Add(_memoryLED); + + // Memory LED Label + var memoryLabel = new TextLabel + { + Text = "Memory:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.DarkGray, + Position = new Position(margin + ledSize + 10, ledYPos - 5), + Size = new Size(150, 30) + }; + mainLayout.Add(memoryLabel); + + // Battery LED (simple colored circle) + _batteryLED = new ImageView + { + Size = new Size(ledSize, ledSize), + Position = new Position(_screenWidth / 2 + margin / 2, ledYPos), + BackgroundColor = Resources.LightGray, + CornerRadius = ledSize / 2.0f + }; + mainLayout.Add(_batteryLED); + + // Battery LED Label + var batteryLabel = new TextLabel + { + Text = "Battery:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.DarkGray, + Position = new Position(_screenWidth / 2 + margin / 2 + ledSize + 10, ledYPos - 5), + Size = new Size(150, 30) + }; + mainLayout.Add(batteryLabel); + + // Language Label + float langYPos = ledYPos + 60; + var languageLabel = new TextLabel + { + Text = "Language:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(margin, langYPos) + }; + mainLayout.Add(languageLabel); + + // Language + _language = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size((_screenWidth - margin * 3) / 2, 40), + Position = new Position(margin, langYPos + 20) + }; + mainLayout.Add(_language); + + // Region Format Label + var regionLabel = new TextLabel + { + Text = "Region:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(_screenWidth / 2 + margin / 2, langYPos) + }; + mainLayout.Add(regionLabel); + + // Region Format + _regionFormat = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size((_screenWidth - margin * 3) / 2, 40), + Position = new Position(_screenWidth / 2 + margin / 2, langYPos + 20) + }; + mainLayout.Add(_regionFormat); + + // Device Orientation Label + float orientYPos = _isLandscape ? langYPos + 80 : _screenHeight - 120; + var orientationLabel = new TextLabel + { + Text = "Orientation:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(margin, orientYPos) + }; + mainLayout.Add(orientationLabel); + + // Device Orientation + _deviceOrientation = new TextLabel + { + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + MultiLine = true, + Size = new Size((_screenWidth - margin * 3) / 2, 40), + Position = new Position(margin, orientYPos + 20) + }; + mainLayout.Add(_deviceOrientation); + + // Rotation Degree Label + var degreeLabel = new TextLabel + { + Text = "Rotation:", + PixelSize = Resources.SmallFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Position = new Position(_screenWidth / 2 + margin / 2, orientYPos) + }; + mainLayout.Add(degreeLabel); + + // Rotation Degree + _rotationDegree = new TextLabel + { + PixelSize = _isLandscape ? 80.0f : Resources.LargeFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top, + Size = new Size((_screenWidth - margin * 3) / 2, _isLandscape ? 100 : 150), + Position = new Position(_screenWidth / 2 + margin / 2, _isLandscape ? orientYPos + 20 : orientYPos - 30) + }; + mainLayout.Add(_rotationDegree); + + // Set content + Content = mainLayout; + } + + private void SetupDataBinding() + { + // Subscribe to PropertyChanged events for manual data binding + _viewModel.PropertyChanged += OnViewModelPropertyChanged; + + // Initialize UI with current ViewModel values + UpdateUI(); + } + + private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) + { + // Update UI directly (NUI is already on UI thread) + switch (e.PropertyName) + { + case nameof(_viewModel.IconPath): + _icon.ResourceUrl = _viewModel.IconPath; + break; + case nameof(_viewModel.Name): + _applicationName.Text = _viewModel.Name; + break; + case nameof(_viewModel.ID): + _applicationID.Text = _viewModel.ID; + break; + case nameof(_viewModel.Version): + _applicationVersion.Text = _viewModel.Version; + break; + case nameof(_viewModel.LowMemoryLEDColor): + _memoryLED.BackgroundColor = _viewModel.LowMemoryLEDColor; + break; + case nameof(_viewModel.LowBatteryLEDColor): + _batteryLED.BackgroundColor = _viewModel.LowBatteryLEDColor; + break; + case nameof(_viewModel.Language): + _language.Text = _viewModel.Language; + break; + case nameof(_viewModel.RegionFormat): + _regionFormat.Text = _viewModel.RegionFormat; + break; + case nameof(_viewModel.DeviceOrientation): + _deviceOrientation.Text = _viewModel.DeviceOrientation; + break; + case nameof(_viewModel.RotationDegree): + _rotationDegree.Text = _viewModel.RotationDegree; + break; + } + } + + private void UpdateUI() + { + _icon.ResourceUrl = _viewModel.IconPath; + _applicationName.Text = _viewModel.Name; + _applicationID.Text = _viewModel.ID; + _applicationVersion.Text = _viewModel.Version; + _memoryLED.BackgroundColor = _viewModel.LowMemoryLEDColor; + _batteryLED.BackgroundColor = _viewModel.LowBatteryLEDColor; + _language.Text = _viewModel.Language; + _regionFormat.Text = _viewModel.RegionFormat; + _deviceOrientation.Text = _viewModel.DeviceOrientation; + _rotationDegree.Text = _viewModel.RotationDegree; + } + + protected override void Dispose(DisposeTypes type) + { + if (type == DisposeTypes.Explicit) + { + // Unsubscribe from events + if (_viewModel != null) + { + _viewModel.PropertyChanged -= OnViewModelPropertyChanged; + } + } + base.Dispose(type); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Pages/MainPage.cs b/IoT/UI/AppCommon/AppCommon/Pages/MainPage.cs new file mode 100755 index 000000000..af4a8c229 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Pages/MainPage.cs @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; + +namespace AppCommon.Pages +{ + /// + /// Main page with tab navigation (simulating ColoredTabbedPage) + /// + public class MainPage : ContentPage + { + private View _currentPageContent; + private ApplicationInformationPage _appInfoPage; + private PathsPage _pathsPage; + private Button _appInfoTab; + private Button _pathsTab; + private View _tabBar; + + private int _screenWidth; + private int _screenHeight; + private bool _isLandscape; + + public ApplicationInformationPage AppInfoPage => _appInfoPage; + + public MainPage(int screenWidth = 720, int screenHeight = 1280) + { + _screenWidth = screenWidth; + _screenHeight = screenHeight; + _isLandscape = screenWidth > screenHeight; + + InitializeComponent(); + ShowPage(0); // Show first page by default + } + + private void InitializeComponent() + { + // Create main layout + var mainLayout = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent + }; + + // Create tab bar (adaptive height based on orientation) + int tabBarHeight = _isLandscape ? 60 : 80; + _tabBar = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = tabBarHeight, + BackgroundColor = Resources.TabbedPageBarColor + }; + + // Create tab buttons + _appInfoTab = new Button + { + Text = "Application Information", + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 1.0f, + BackgroundColor = Resources.TabbedPageBarColor, + TextColor = Resources.White + }; + _appInfoTab.Clicked += (s, e) => ShowPage(0); + + _pathsTab = new Button + { + Text = "Paths", + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 1.0f, + BackgroundColor = Resources.TabbedPageBarColor, + TextColor = Resources.White + }; + _pathsTab.Clicked += (s, e) => ShowPage(1); + + _tabBar.Add(_appInfoTab); + _tabBar.Add(_pathsTab); + + // Create content container + _currentPageContent = new View + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent + }; + + // Add tab bar and content to main layout + mainLayout.Add(_tabBar); + mainLayout.Add(_currentPageContent); + + // Set content + Content = mainLayout; + + // Create pages + _appInfoPage = new ApplicationInformationPage(_screenWidth, _screenHeight); + _pathsPage = new PathsPage(_screenWidth, _screenHeight); + } + + private void ShowPage(int index) + { + // Clear current content + if (_currentPageContent.ChildCount > 0) + { + _currentPageContent.Remove(_currentPageContent.GetChildAt(0)); + } + + // Update tab button states + _appInfoTab.BackgroundColor = (index == 0) + ? new Color(0.6f, 0.15f, 0.4f, 1.0f) // Darker shade for selected + : Resources.TabbedPageBarColor; + + _pathsTab.BackgroundColor = (index == 1) + ? new Color(0.6f, 0.15f, 0.4f, 1.0f) // Darker shade for selected + : Resources.TabbedPageBarColor; + + // Show selected page content + switch (index) + { + case 0: + if (_appInfoPage.Content != null) + { + _currentPageContent.Add(_appInfoPage.Content); + } + break; + case 1: + if (_pathsPage.Content != null) + { + _currentPageContent.Add(_pathsPage.Content); + } + break; + } + } + + protected override void Dispose(DisposeTypes type) + { + if (type == DisposeTypes.Explicit) + { + _appInfoPage?.Dispose(); + _pathsPage?.Dispose(); + } + base.Dispose(type); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Pages/PathsPage.cs b/IoT/UI/AppCommon/AppCommon/Pages/PathsPage.cs new file mode 100755 index 000000000..62ac409a9 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Pages/PathsPage.cs @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025 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.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using AppCommon.Models; +using AppCommon.ViewModels; + +namespace AppCommon.Pages +{ + /// + /// Paths information page + /// + public class PathsPage : ContentPage + { + private PathsPageViewModel _viewModel; + private ScrollableBase _scrollView; + private View _listContainer; + private Button _floatingButton; + private AlertDialog _popup; + + private int _screenWidth; + private int _screenHeight; + private double _horizontalScale; + private bool _isLandscape; + + public PathsPage(int screenWidth = 720, int screenHeight = 1280) + { + _screenWidth = screenWidth; + _screenHeight = screenHeight; + _isLandscape = screenWidth > screenHeight; + _horizontalScale = (double)screenWidth / 720.0; + + // Create ViewModel + _viewModel = new PathsPageViewModel(); + + InitializeComponent(); + } + + private void InitializeComponent() + { + // Set page properties + AppBar = new AppBar + { + Title = "Paths" + }; + + // Create main layout with simple background + var mainLayout = new View + { + Layout = new AbsoluteLayout(), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + BackgroundColor = new Color(0.95f, 0.95f, 0.95f, 1.0f) // Light gray background + }; + + // Create scrollable list container + _scrollView = new ScrollableBase + { + ScrollingDirection = ScrollableBase.Direction.Vertical, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + HideScrollbar = false + }; + + _listContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + + // Add path items to list + foreach (var pathInfo in _viewModel.Paths) + { + var itemView = CreatePathItemView(pathInfo); + _listContainer.Add(itemView); + } + + _scrollView.Add(_listContainer); + mainLayout.Add(_scrollView); + + // Create floating button (simple text button) + _floatingButton = new Button + { + Text = "↑", + Size = new Size((float)(100 * _horizontalScale), (float)(100 * _horizontalScale)), + Position = new Position(_screenWidth * 0.8194f, _screenHeight * 0.8948f), + BackgroundColor = Resources.TabbedPageBarColor, + TextColor = Color.White, + CornerRadius = (float)(50 * _horizontalScale) + }; + _floatingButton.Clicked += OnFloatingButtonClicked; + mainLayout.Add(_floatingButton); + + // Set content + Content = mainLayout; + } + + private View CreatePathItemView(PathInformation pathInfo) + { + int itemHeight = (int)(227.0 * _horizontalScale); + + var itemView = new View + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = itemHeight, + BackgroundColor = Color.White, + Margin = new Extents(0, 0, 2, 2), + Focusable = true, + FocusableInTouch = true, + BoxShadow = new Shadow(5.0f, new Color(0, 0, 0, 0.2f), new Vector2(0, 2)) + }; + + var itemLayout = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(10, 10), + HorizontalAlignment = HorizontalAlignment.Begin, + VerticalAlignment = VerticalAlignment.Top + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Padding = new Extents(20, 20, 10, 10) + }; + + // Title + var titleLabel = new TextLabel + { + Text = pathInfo.Title, + PixelSize = 30.0f, + TextColor = Resources.Black, + FontStyle = new PropertyMap().Add("weight", new PropertyValue("bold")), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + itemLayout.Add(titleLabel); + + // Path + var pathLabel = new TextLabel + { + Text = pathInfo.Path, + PixelSize = Resources.NormalFontSize, + TextColor = Resources.DarkGray, + MultiLine = true, + Ellipsis = true, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + itemLayout.Add(pathLabel); + + itemView.Add(itemLayout); + + // Add touch event + itemView.TouchEvent += (sender, e) => + { + if (e.Touch.GetState(0) == PointStateType.Up) + { + OnListItemSelected(pathInfo); + return true; + } + return false; + }; + + return itemView; + } + + private void OnListItemSelected(PathInformation selectedItem) + { + var title = selectedItem.Title; + var path = selectedItem.Path; + var count = 0; + + try + { + count = _viewModel.GetFilesCount(path); + } + catch + { + count = 0; + } + + // Create popup dialog + ShowPathDetailsDialog(title, path, count); + } + + private void ShowPathDetailsDialog(string title, string path, int count) + { + _popup = new AlertDialog + { + Title = title, + }; + + var contentView = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(10, 10) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Padding = new Extents(20, 20, 20, 20) + }; + + var pathLabel = new TextLabel + { + Text = path, + PixelSize = Resources.NormalFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Center, + MultiLine = true, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + contentView.Add(pathLabel); + + var countLabel = new TextLabel + { + Text = $"File count: {count}", + PixelSize = Resources.NormalFontSize, + TextColor = Resources.Black, + HorizontalAlignment = HorizontalAlignment.Center, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + contentView.Add(countLabel); + + _popup.Content = contentView; + + var closeButton = new Button + { + Text = "CLOSE", + }; + closeButton.Clicked += (sender, e) => + { + _popup.Hide(); + }; + + _popup.Actions = new View[] { closeButton }; + _popup.Show(); + } + + private void OnFloatingButtonClicked(object sender, ClickedEventArgs e) + { + // Scroll to top + _scrollView.ScrollTo(0, true); + } + + protected override void Dispose(DisposeTypes type) + { + if (type == DisposeTypes.Explicit) + { + if (_floatingButton != null) + { + _floatingButton.Clicked -= OnFloatingButtonClicked; + } + } + base.Dispose(type); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Resources.cs b/IoT/UI/AppCommon/AppCommon/Resources.cs new file mode 100755 index 000000000..e4e1a483f --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Resources.cs @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; + +namespace AppCommon +{ + /// + /// Common resources for the application (colors, styles, etc.) + /// + internal static class Resources + { + // Colors + public static readonly Color White = new Color(1.0f, 1.0f, 1.0f, 1.0f); + public static readonly Color Black = new Color(0.0f, 0.0f, 0.0f, 1.0f); + public static readonly Color Gray = new Color(0.5f, 0.5f, 0.5f, 1.0f); + public static readonly Color LightGray = new Color(0.875f, 0.875f, 0.875f, 1.0f); // RGB(223, 223, 223) + public static readonly Color DarkGray = new Color(0.573f, 0.573f, 0.573f, 1.0f); // RGB(146, 146, 146) + public static readonly Color TabbedPageBarColor = new Color(0.706f, 0.204f, 0.498f, 1.0f); // RGB(180, 52, 127) + public static readonly Color Red = new Color(1.0f, 0.0f, 0.0f, 1.0f); + public static readonly Color Green = new Color(0.0f, 1.0f, 0.0f, 1.0f); + public static readonly Color Yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f); + + // Font sizes + public const float SmallFontSize = 20.0f; + public const float NormalFontSize = 25.0f; + public const float LargeFontSize = 130.0f; + + // TextLabel Styles + public static readonly TextLabelStyle ContentTextStyle = new TextLabelStyle + { + PixelSize = NormalFontSize, + TextColor = DarkGray, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Begin, + MultiLine = true, + Ellipsis = false, + }; + + public static readonly TextLabelStyle LargeContentTextStyle = new TextLabelStyle + { + PixelSize = LargeFontSize, + TextColor = Black, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Begin, + }; + + // Button Styles + public static readonly ButtonStyle PrimaryButtonStyle = new ButtonStyle + { + BackgroundColor = new Selector + { + Normal = TabbedPageBarColor, + Pressed = new Color(0.6f, 0.15f, 0.4f, 1.0f), + }, + Text = new TextLabelStyle + { + TextColor = White, + PixelSize = NormalFontSize, + } + }; + + // Helper method to create TextLabel with content style + public static TextLabel CreateContentLabel(string text) + { + return new TextLabel + { + Text = text, + PixelSize = NormalFontSize, + TextColor = DarkGray, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Begin, + MultiLine = true, + Ellipsis = false, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + } + + // Helper method to create TextLabel with large content style + public static TextLabel CreateLargeContentLabel(string text) + { + return new TextLabel + { + Text = text, + PixelSize = LargeFontSize, + TextColor = Black, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Begin, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/AppInformationService.cs b/IoT/UI/AppCommon/AppCommon/Services/AppInformationService.cs new file mode 100755 index 000000000..be62cd576 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/AppInformationService.cs @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 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 Tizen.Applications; + +namespace AppCommon.Services +{ + /// + /// Implementation of application information service + /// + public class AppInformationService : IAppInformationService + { + private static readonly Application Current = Application.Current; + + public string ID + { + get { return Current.ApplicationInfo.ApplicationId; } + } + + public string Name + { + get { return Current.ApplicationInfo.Label; } + } + + public string IconPath + { + get { return Current.ApplicationInfo.IconPath; } + } + + public string CachePath + { + get { return Current.DirectoryInfo.Cache; } + } + + public string ExternalCachePath + { + get { return Current.DirectoryInfo.ExternalCache; } + } + + public string ExternalDataPath + { + get { return Current.DirectoryInfo.ExternalData; } + } + + public string ExternalSharedDataPath + { + get { return Current.DirectoryInfo.ExternalSharedData; } + } + + public string ResourcePath + { + get { return Current.DirectoryInfo.Resource; } + } + + public string ResourcesPath + { + get { return Current.DirectoryInfo.Resource; } + } + + public string SharedDataPath + { + get { return Current.ApplicationInfo.SharedDataPath; } + } + + public string SharedResourcePath + { + get { return Current.ApplicationInfo.SharedResourcePath; } + } + + public string SharedTrustedPath + { + get { return Current.ApplicationInfo.SharedTrustedPath; } + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/BatteryInformationService.cs b/IoT/UI/AppCommon/AppCommon/Services/BatteryInformationService.cs new file mode 100755 index 000000000..031d3cfc1 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/BatteryInformationService.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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 AppCommon.Models; +using TBattery = Tizen.System.Battery; + +namespace AppCommon.Services +{ + /// + /// Implementation of battery information service + /// + public class BatteryInformationService : IBatteryInformationService + { + public BatteryInformationService() + { + TBattery.PercentChanged += (s, e) => + { + var status = LowBatteryStatus.None; + if (e.Percent > 5) + { + status = LowBatteryStatus.None; + } + else if (e.Percent > 0) + { + status = LowBatteryStatus.CriticalLow; + } + else + { + status = LowBatteryStatus.PowerOff; + } + OnLevelChanged(new BatteryLevelChangedEventArgs(status)); + }; + } + + public event EventHandler LevelChanged; + + void OnLevelChanged(BatteryLevelChangedEventArgs e) + { + LevelChanged?.Invoke(this, e); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/DirectoryService.cs b/IoT/UI/AppCommon/AppCommon/Services/DirectoryService.cs new file mode 100755 index 000000000..c9499589b --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/DirectoryService.cs @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 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.IO; + +namespace AppCommon.Services +{ + /// + /// Implementation of directory service + /// + public class DirectoryService : IDirectoryService + { + public bool Exists(string path) + { + return Directory.Exists(path); + } + + public string[] GetFiles(string path) + { + return Directory.GetFiles(path); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/IAppInformationService.cs b/IoT/UI/AppCommon/AppCommon/Services/IAppInformationService.cs new file mode 100755 index 000000000..73aa7ab1f --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/IAppInformationService.cs @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +namespace AppCommon.Services +{ + /// + /// Interface for application information service + /// + public interface IAppInformationService + { + string ID { get; } + string Name { get; } + string IconPath { get; } + string CachePath { get; } + string ExternalCachePath { get; } + string ExternalDataPath { get; } + string ExternalSharedDataPath { get; } + string ResourcePath { get; } + string ResourcesPath { get; } + string SharedDataPath { get; } + string SharedResourcePath { get; } + string SharedTrustedPath { get; } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/IBatteryInformationService.cs b/IoT/UI/AppCommon/AppCommon/Services/IBatteryInformationService.cs new file mode 100755 index 000000000..949442195 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/IBatteryInformationService.cs @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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 AppCommon.Models; + +namespace AppCommon.Services +{ + public class BatteryLevelChangedEventArgs : EventArgs + { + public BatteryLevelChangedEventArgs(LowBatteryStatus level) + { + LowBatteryStatus = level; + } + + public LowBatteryStatus LowBatteryStatus { get; private set; } + } + + /// + /// Interface for battery information service + /// + public interface IBatteryInformationService + { + event EventHandler LevelChanged; + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/IDirectoryService.cs b/IoT/UI/AppCommon/AppCommon/Services/IDirectoryService.cs new file mode 100755 index 000000000..1302a1bed --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/IDirectoryService.cs @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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. + */ + +namespace AppCommon.Services +{ + /// + /// Directory service interface + /// + public interface IDirectoryService + { + /// + /// Returns the names of files (including their paths) in the specified directory. + /// + string[] GetFiles(string path); + + /// + /// Determines whether the given path refers to an existing directory on disk. + /// + bool Exists(string path); + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/ISystemSettingsService.cs b/IoT/UI/AppCommon/AppCommon/Services/ISystemSettingsService.cs new file mode 100755 index 000000000..665aaeb48 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/ISystemSettingsService.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +namespace AppCommon.Services +{ + /// + /// System settings service interface + /// + public interface ISystemSettingsService + { + string Language { get; } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/ServiceLocator.cs b/IoT/UI/AppCommon/AppCommon/Services/ServiceLocator.cs new file mode 100755 index 000000000..674ea3d81 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/ServiceLocator.cs @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 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; + +namespace AppCommon.Services +{ + /// + /// Simple service locator to replace Xamarin.Forms DependencyService + /// + public static class ServiceLocator + { + private static readonly Dictionary _services = new Dictionary(); + + /// + /// Register a service implementation + /// + public static void Register(TInterface implementation) + { + _services[typeof(TInterface)] = implementation; + } + + /// + /// Get a registered service + /// + public static TInterface Get() + { + if (_services.TryGetValue(typeof(TInterface), out var service)) + { + return (TInterface)service; + } + throw new InvalidOperationException($"Service {typeof(TInterface).Name} not registered"); + } + + /// + /// Initialize all services + /// + public static void Initialize() + { + Register(new AppInformationService()); + Register(new BatteryInformationService()); + Register(new DirectoryService()); + Register(new SystemSettingsService()); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/Services/SystemSettingsService.cs b/IoT/UI/AppCommon/AppCommon/Services/SystemSettingsService.cs new file mode 100755 index 000000000..5f21e2b38 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/Services/SystemSettingsService.cs @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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.Globalization; +using TSystem = Tizen.System; + +namespace AppCommon.Services +{ + /// + /// Implementation of system settings service + /// + public class SystemSettingsService : ISystemSettingsService + { + public string Language + { + get + { + return new CultureInfo(TSystem.SystemSettings.LocaleLanguage).DisplayName; + } + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/ViewModels/ApplicationInformationViewModel.cs b/IoT/UI/AppCommon/AppCommon/ViewModels/ApplicationInformationViewModel.cs new file mode 100755 index 000000000..749a0cf5a --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/ViewModels/ApplicationInformationViewModel.cs @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2025 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.ComponentModel; +using Tizen.NUI; +using AppCommon.Models; +using AppCommon.Services; + +namespace AppCommon.ViewModels +{ + /// + /// ViewModel for application information page + /// + public class ApplicationInformationViewModel : INotifyPropertyChanged + { + private const string degree = "\u00b0"; + + private string _id; + private string _name; + private string _iconPath; + private string _version; + private Color _lowBatteryLEDColor; + private Color _lowMemoryLEDColor; + private string _language; + private string _regionFormat; + private string _deviceOrientation; + private string _rotationDegree; + + public ApplicationInformationViewModel() + { + Initialize(); + } + + public string ID + { + get => _id; + set + { + if (_id != value) + { + _id = value; + OnPropertyChanged(nameof(ID)); + } + } + } + + public string Name + { + get => _name; + set + { + if (_name != value) + { + _name = value; + OnPropertyChanged(nameof(Name)); + } + } + } + + public string IconPath + { + get => _iconPath; + set + { + if (_iconPath != value) + { + _iconPath = value; + OnPropertyChanged(nameof(IconPath)); + } + } + } + + public string Version + { + get => _version; + set + { + if (_version != value) + { + _version = value; + OnPropertyChanged(nameof(Version)); + } + } + } + + public Color LowBatteryLEDColor + { + get => _lowBatteryLEDColor; + set + { + if (_lowBatteryLEDColor != value) + { + _lowBatteryLEDColor = value; + OnPropertyChanged(nameof(LowBatteryLEDColor)); + } + } + } + + public Color LowMemoryLEDColor + { + get => _lowMemoryLEDColor; + set + { + if (_lowMemoryLEDColor != value) + { + _lowMemoryLEDColor = value; + OnPropertyChanged(nameof(LowMemoryLEDColor)); + } + } + } + + public string Language + { + get => _language; + set + { + if (_language != value) + { + _language = value; + OnPropertyChanged(nameof(Language)); + } + } + } + + public string RegionFormat + { + get => _regionFormat; + set + { + if (_regionFormat != value) + { + _regionFormat = value; + OnPropertyChanged(nameof(RegionFormat)); + } + } + } + + public string DeviceOrientation + { + get => _deviceOrientation; + set + { + if (_deviceOrientation != value) + { + _deviceOrientation = value; + OnPropertyChanged(nameof(DeviceOrientation)); + } + } + } + + public string RotationDegree + { + get => _rotationDegree; + set + { + if (_rotationDegree != value) + { + _rotationDegree = value; + OnPropertyChanged(nameof(RotationDegree)); + } + } + } + + public void UpdateLowBatteryLEDColor(LowBatteryStatus status) + { + switch (status) + { + case LowBatteryStatus.PowerOff: + LowBatteryLEDColor = Resources.Red; + return; + case LowBatteryStatus.CriticalLow: + LowBatteryLEDColor = Resources.Yellow; + return; + case LowBatteryStatus.None: + default: + LowBatteryLEDColor = Resources.LightGray; + return; + } + } + + public void UpdateLowMemoryLEDColor(LowMemoryStatus status) + { + switch (status) + { + case LowMemoryStatus.HardWarning: + LowMemoryLEDColor = Resources.Red; + return; + case LowMemoryStatus.SoftWarning: + LowMemoryLEDColor = Resources.Yellow; + return; + case LowMemoryStatus.Normal: + LowMemoryLEDColor = Resources.Green; + return; + case LowMemoryStatus.None: + default: + LowMemoryLEDColor = Resources.LightGray; + return; + } + } + + public void UpdateDeviceOrientation(DeviceOrientationStatus orientation) + { + switch (orientation) + { + case DeviceOrientationStatus.Orientation_0: + DeviceOrientation = "Natural position"; + RotationDegree = "0" + degree; + return; + case DeviceOrientationStatus.Orientation_90: + DeviceOrientation = "Right-side up"; + RotationDegree = "90" + degree; + return; + case DeviceOrientationStatus.Orientation_180: + DeviceOrientation = "Up-side down"; + RotationDegree = "180" + degree; + return; + case DeviceOrientationStatus.Orientation_270: + DeviceOrientation = "Left-side up"; + RotationDegree = "270" + degree; + return; + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + void Initialize() + { + var appInfo = ServiceLocator.Get(); + var systemSettings = ServiceLocator.Get(); + var batteryInfo = ServiceLocator.Get(); + + batteryInfo.LevelChanged += (s, e) => + { + UpdateLowBatteryLEDColor(e.LowBatteryStatus); + }; + + ID = appInfo.ID; + Name = appInfo.Name; + IconPath = appInfo.IconPath; + + Version = "0.0.0.1"; + LowBatteryLEDColor = Resources.LightGray; + LowMemoryLEDColor = Resources.LightGray; + Language = systemSettings.Language; + RegionFormat = systemSettings.Language; + + DeviceOrientation = "Natural position"; + RotationDegree = "0" + degree; + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/ViewModels/PathsPageViewModel.cs b/IoT/UI/AppCommon/AppCommon/ViewModels/PathsPageViewModel.cs new file mode 100755 index 000000000..b09ef1fff --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/ViewModels/PathsPageViewModel.cs @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025 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.Collections.Generic; +using AppCommon.Models; +using AppCommon.Services; + +namespace AppCommon.ViewModels +{ + /// + /// ViewModel for paths page + /// + public class PathsPageViewModel + { + private IAppInformationService _appInfo; + private IDirectoryService _directory; + + public PathsPageViewModel() + { + Initialize(); + } + + public List Paths { get; set; } + + public int GetFilesCount(string path) + { + try + { + return _directory.GetFiles(path).Length; + } + catch + { + return 0; + } + } + + void Initialize() + { + Paths = new List(); + _appInfo = ServiceLocator.Get(); + _directory = ServiceLocator.Get(); + + SetListItems(); + } + + private void SetListItems() + { + Paths.Add(new PathInformation + { + Title = "Resources", + Path = _appInfo.ResourcesPath + }); + + Paths.Add(new PathInformation + { + Title = "Cache", + Path = _appInfo.CachePath + }); + + Paths.Add(new PathInformation + { + Title = "Shared Data", + Path = _appInfo.SharedDataPath + }); + + Paths.Add(new PathInformation + { + Title = "Shared Resource", + Path = _appInfo.SharedResourcePath + }); + + Paths.Add(new PathInformation + { + Title = "Shared Trusted", + Path = _appInfo.SharedTrustedPath + }); + + Paths.Add(new PathInformation + { + Title = "External Data", + Path = _appInfo.ExternalDataPath + }); + + Paths.Add(new PathInformation + { + Title = "External Cache", + Path = _appInfo.ExternalCachePath + }); + + Paths.Add(new PathInformation + { + Title = "External Shared Data", + Path = _appInfo.ExternalSharedDataPath + }); + } + } +} diff --git a/IoT/UI/AppCommon/AppCommon/res/background_app.png b/IoT/UI/AppCommon/AppCommon/res/background_app.png new file mode 100755 index 000000000..7795f2c15 Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon/res/background_app.png differ diff --git a/IoT/UI/AppCommon/AppCommon/res/led.png b/IoT/UI/AppCommon/AppCommon/res/led.png new file mode 100755 index 000000000..963545874 Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon/res/led.png differ diff --git a/IoT/UI/AppCommon/AppCommon/res/list_item_bg.png b/IoT/UI/AppCommon/AppCommon/res/list_item_bg.png new file mode 100755 index 000000000..5f1e601fe Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon/res/list_item_bg.png differ diff --git a/IoT/UI/AppCommon/AppCommon/res/list_top_icon.png b/IoT/UI/AppCommon/AppCommon/res/list_top_icon.png new file mode 100755 index 000000000..008059c87 Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon/res/list_top_icon.png differ diff --git a/IoT/UI/AppCommon/AppCommon/shared/res/AppCommon.png b/IoT/UI/AppCommon/AppCommon/shared/res/AppCommon.png new file mode 100755 index 000000000..9f3cb9860 Binary files /dev/null and b/IoT/UI/AppCommon/AppCommon/shared/res/AppCommon.png differ diff --git a/IoT/UI/AppCommon/AppCommon/tizen-manifest.xml b/IoT/UI/AppCommon/AppCommon/tizen-manifest.xml new file mode 100755 index 000000000..9aae38fa6 --- /dev/null +++ b/IoT/UI/AppCommon/AppCommon/tizen-manifest.xml @@ -0,0 +1,8 @@ + + + + + + AppCommon.png + + diff --git a/IoT/UI/ApplicationControl/ApplicationControl.png b/IoT/UI/ApplicationControl/ApplicationControl.png new file mode 100755 index 000000000..2b1b85287 Binary files /dev/null and b/IoT/UI/ApplicationControl/ApplicationControl.png differ diff --git a/IoT/UI/ApplicationControl/ApplicationControl.sln b/IoT/UI/ApplicationControl/ApplicationControl.sln new file mode 100755 index 000000000..0833907cf --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationControl", "ApplicationControl\ApplicationControl.csproj", "{ECC6B993-E617-4D8E-8789-026A99CE3C86}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECC6B993-E617-4D8E-8789-026A99CE3C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECC6B993-E617-4D8E-8789-026A99CE3C86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECC6B993-E617-4D8E-8789-026A99CE3C86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECC6B993-E617-4D8E-8789-026A99CE3C86}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A37209D3-A9C7-4D51-A0C2-4F7F760481F4} + EndGlobalSection +EndGlobal diff --git a/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.cs b/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.cs new file mode 100755 index 000000000..b6dbd1eb5 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.cs @@ -0,0 +1,39 @@ +using System; +using Tizen.NUI; +using Tizen.NUI.Components; +using Tizen.NUI.BaseComponents; +using ApplicationControl.Pages; + +namespace ApplicationControl +{ + class Program : NUIApplication + { + private Window window; + private Navigator navigator; + + protected override void OnCreate() + { + base.OnCreate(); + // Disable XAML usage + IsUsingXaml = false; + Initialize(); + } + + void Initialize() + { + window = NUIApplication.GetDefaultWindow(); + window.BackgroundColor = Color.White; + + // Get default navigator and push MainPage + navigator = window.GetDefaultNavigator(); + var mainPage = new MainPage(); + navigator.Push(mainPage); + } + + static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.csproj b/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.csproj new file mode 100755 index 000000000..0aae0055f --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/ApplicationControl.csproj @@ -0,0 +1,27 @@ + + + + + Exe + net6.0-tizen + + + + portable + + + None + + + + + + + + + True + + + + + diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Helpers/AppControlType.cs b/IoT/UI/ApplicationControl/ApplicationControl/Helpers/AppControlType.cs new file mode 100755 index 000000000..80ca95033 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Helpers/AppControlType.cs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 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. + */ + +namespace ApplicationControl.Helpers +{ + /// + /// An enumeration for appcontrol type + /// + public enum AppControlType + { + View, + Pick, + Compose, + Send, + Unknown, + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Helpers/ApplicationControlHelper.cs b/IoT/UI/ApplicationControl/ApplicationControl/Helpers/ApplicationControlHelper.cs new file mode 100755 index 000000000..44692496f --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Helpers/ApplicationControlHelper.cs @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2017 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.Diagnostics; +using Tizen.Applications; +using ApplicationControl.Models; + +namespace ApplicationControl.Helpers +{ + /// + /// A singleton class to manipulate application operation + /// + public class ApplicationOperations + { + static ApplicationOperations _instance; + Dictionary _launchedAppControl; + + ApplicationOperations() + { + _launchedAppControl = new Dictionary(); + } + + static public ApplicationOperations Instance + { + get + { + if (_instance == null) + { + _instance = new ApplicationOperations(); + } + + return _instance; + } + } + + /// + /// To get the selected operation id + /// + /// selected operation name + /// selected operation id + public IEnumerable GetMatchedApplicationIds(string operation) + { + AppControl appControl = new AppControl + { + Operation = operation, + LaunchMode = AppControlLaunchMode.Group + }; + if (operation.Equals(AppControlOperations.View)) + { + appControl.Uri = "http"; + } + + return AppControl.GetMatchedApplicationIds(appControl); + } + + /// + /// To send launch request + /// + /// selected operation + /// selected operation app + /// a message to send + public void SendLaunchRequest(string operation, string id, Message data = null) + { + var appCon = new AppControl + { + Operation = operation, + LaunchMode = AppControlLaunchMode.Group + }; + + if (data != null) + { + if (!String.IsNullOrEmpty(data.To)) + { + appCon.ExtraData.Add(AppControlData.To, data.To); + } + + appCon.ExtraData.Add(AppControlData.Subject, data.Subject); + appCon.ExtraData.Add(AppControlData.Text, data.Text); + } + + if (!string.IsNullOrEmpty(id)) + { + appCon.ApplicationId = id; + SaveLaunchedAppControl(appCon); + } + + try + { + AppControl.SendLaunchRequest(appCon); + } + catch + { + /// Error Handling Codes + } + } + + /// + /// To send a terminate request + /// + /// a selected application ID + public void SendTerminateRequest(string id) + { + var appCon = GetLaunchedAppControl(id); + if (appCon == null) + { + return; + } + + try + { + AppControl.SendTerminateRequest(appCon); + RemoveLaunchedAppControl(appCon.ApplicationId); + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + } + } + + /// + /// To get the application icon path + /// + /// An application ID + /// The application icon path + public string GetApplicationIconPath(string id) + { + var info = new ApplicationInfo(id); + string iconPath = info.IconPath; + info.Dispose(); + return iconPath; + } + + /// + /// The view operation + /// + public string View => AppControlOperations.View; + + /// + /// The pick operation + /// + public string Pick => AppControlOperations.Pick; + + /// + /// The compose operation + /// + public string Compose => AppControlOperations.Compose; + + /// + /// The send operation + /// + public string Send => AppControlOperations.Send; + + /// + /// To save a launched appcontrol that it will be used to send a kill request later + /// + /// An application control to save + void SaveLaunchedAppControl(AppControl appCon) + { + if (!_launchedAppControl.ContainsKey(appCon.ApplicationId)) + { + _launchedAppControl.Add(appCon.ApplicationId, appCon); + } + } + + /// + /// To get the application control by an application ID + /// + /// An application ID + /// An application control + AppControl GetLaunchedAppControl(string id) + { + if (!_launchedAppControl.ContainsKey(id)) + { + return null; + } + + return _launchedAppControl[id]; + } + + /// + /// To remove an application control by an application ID + /// + /// An application ID + void RemoveLaunchedAppControl(string id) + { + _launchedAppControl.Remove(id); + } + } + + /// + /// A static class to manipulate the application control and the application information functionalities + /// + static public class ApplicationControlHelper + { + /// + /// To get application IDs matched a specific appcontrol type + /// + /// an appcontrol type + /// application IDs + public static IEnumerable GetApplicationIdsForSpecificAppControlType(AppControlType type) + { + return ApplicationOperations.Instance.GetMatchedApplicationIds(IntToString(type)); + } + + /// + /// To get the application icon path for an application + /// + /// an application ID + /// the application icon path + public static string GetApplicationIconPath(string id) + { + return ApplicationOperations.Instance.GetApplicationIconPath(id); + } + + /// + /// To send a launch request + /// + /// an appcontrol type + /// an application ID + /// a message to pass with an appcontrol + public static void ExecuteApplication(AppControlType type, string id, Message data = null) + { + ApplicationOperations.Instance.SendLaunchRequest(IntToString(type), id, data); + } + + /// + /// To send a terminate request + /// + /// an application ID + public static void KillApplication(string id) + { + ApplicationOperations.Instance.SendTerminateRequest(id); + } + + /// + /// To change an enumeration to a matched string + /// + /// An enumeration + /// A matched string + static string IntToString(AppControlType type) + { + var operation = ApplicationOperations.Instance; + switch (type) + { + case AppControlType.View: + { + return operation.View; + } + + case AppControlType.Pick: + { + return operation.Pick; + } + + case AppControlType.Compose: + { + return operation.Compose; + } + + case AppControlType.Send: + { + return operation.Send; + } + + default: + { + return "Unknown"; + } + } + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Models/ApplicationListItem.cs b/IoT/UI/ApplicationControl/ApplicationControl/Models/ApplicationListItem.cs new file mode 100755 index 000000000..c13853fe7 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Models/ApplicationListItem.cs @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 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.ComponentModel; +using Tizen.NUI; + +namespace ApplicationControl.Models +{ + /// + /// A class for an application list item + /// + public class ApplicationListItem : INotifyPropertyChanged + { + Color _blendColor; + + /// + /// The application ID + /// + public string Id { get; set; } + + /// + /// The icon path + /// + public string IconPath { get; set; } + + /// + /// The color be blended with an image + /// + public Color BlendColor + { + get + { + return _blendColor; + } + + set + { + if (_blendColor == value) + { + return; + } + + _blendColor = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BlendColor")); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Models/Message.cs b/IoT/UI/ApplicationControl/ApplicationControl/Models/Message.cs new file mode 100755 index 000000000..bb41e7863 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Models/Message.cs @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 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. + */ + +namespace ApplicationControl.Models +{ + /// + /// A class for a composed message will be sent + /// + public class Message + { + /// + /// A constructor for the Message class + /// + public Message() + { + Initialize(); + } + + /// + /// An email address to send + /// + public string To { get; set; } + + /// + /// An Subject for the message + /// + public string Subject { get; set; } + + /// + /// An text content to send + /// + public string Text { get; set; } + + void Initialize() + { + Subject = "Message from appcontrol"; + Text = "Dear Developer,\n\nThis is the default message sent from\nappcontrol sample application.\nFeel free to modify this text message in email composer.\n\nBest Regards."; + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Pages/MainPage.cs b/IoT/UI/ApplicationControl/ApplicationControl/Pages/MainPage.cs new file mode 100755 index 000000000..fdfcccf59 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Pages/MainPage.cs @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using ApplicationControl.ViewModels; +using ApplicationControl.Views; + +namespace ApplicationControl.Pages +{ + /// + /// Main page for ApplicationControl + /// + public class MainPage : ContentPage + { + private MainViewModel _viewModel; + private OperationLayout _operationLayout; + private ApplicationLayout _applicationLayout; + private ComposeLayout _composeLayout; + + public MainPage() + { + _viewModel = new MainViewModel(); + InitializeComponent(); + } + + void InitializeComponent() + { + // Set page background + BackgroundColor = Resources.BackgroundLight; + + // Get screen dimensions + var window = NUIApplication.GetDefaultWindow(); + var screenWidth = (int)window.WindowSize.Width; + var screenHeight = (int)window.WindowSize.Height; + + // Horizontal layout for landscape view + // Left panel: Operation selection (30%) + // Right panel: Application list and compose (70%) + var mainLayout = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + CellPadding = new Size2D(Resources.Dimensions.Padding, 0) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding) + }; + + // Left Panel: Operation Selection + _operationLayout = new OperationLayout(_viewModel) + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 0.3f + }; + + // Right Panel Container + var rightPanel = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, Resources.Dimensions.Padding) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 0.7f + }; + + // Application List (60% of right panel) + _applicationLayout = new ApplicationLayout(_viewModel, screenWidth, screenHeight) + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 0.6f + }; + + // Compose Section (40% of right panel) + _composeLayout = new ComposeLayout(_viewModel) + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 0.4f + }; + + rightPanel.Add(_applicationLayout); + rightPanel.Add(_composeLayout); + + // Add panels to main layout + mainLayout.Add(_operationLayout); + mainLayout.Add(rightPanel); + + // Set content + Content = mainLayout; + + // Set AppBar + AppBar = new AppBar + { + Title = "Application Control", + AutoNavigationContent = false + }; + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Resources.cs b/IoT/UI/ApplicationControl/ApplicationControl/Resources.cs new file mode 100755 index 000000000..cd77c6ea1 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Resources.cs @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; + +namespace ApplicationControl +{ + /// + /// Static resource class for ApplicationControl + /// + internal static class Resources + { + // Colors - Modern Color Scheme + public static readonly Color White = new Color(1.0f, 1.0f, 1.0f, 1.0f); + public static readonly Color Black = new Color(0.0f, 0.0f, 0.0f, 1.0f); + public static readonly Color Gray = new Color(0.5f, 0.5f, 0.5f, 1.0f); + public static readonly Color LightGray = new Color(0.95f, 0.95f, 0.95f, 1.0f); + public static readonly Color DarkGray = new Color(0.3f, 0.3f, 0.3f, 1.0f); + public static readonly Color Transparent = new Color(0.0f, 0.0f, 0.0f, 0.0f); + + // Primary Colors + public static readonly Color PrimaryBlue = new Color(0.26f, 0.52f, 0.96f, 1.0f); // #4285F4 + public static readonly Color PrimaryBlueDark = new Color(0.2f, 0.4f, 0.8f, 1.0f); + public static readonly Color PrimaryBlueLight = new Color(0.67f, 0.8f, 0.98f, 1.0f); + + // Accent Colors + public static readonly Color AccentGreen = new Color(0.2f, 0.73f, 0.29f, 1.0f); // #34BA52 + public static readonly Color AccentRed = new Color(0.92f, 0.26f, 0.21f, 1.0f); // #EA4335 + public static readonly Color AccentOrange = new Color(1.0f, 0.67f, 0.0f, 1.0f); // #FFAB00 + + // Background Colors + public static readonly Color BackgroundLight = new Color(0.98f, 0.98f, 0.98f, 1.0f); + public static readonly Color CardBackground = new Color(1.0f, 1.0f, 1.0f, 1.0f); + public static readonly Color SectionBackground = new Color(0.96f, 0.96f, 0.96f, 1.0f); + + // Text Colors + public static readonly Color TextPrimary = new Color(0.13f, 0.13f, 0.13f, 1.0f); + public static readonly Color TextSecondary = new Color(0.45f, 0.45f, 0.45f, 1.0f); + public static readonly Color TextHint = new Color(0.62f, 0.62f, 0.62f, 1.0f); + + // State Colors + public static readonly Color PressedColor = new Color(0.0f, 0.0f, 0.0f, 0.1f); + public static readonly Color SelectedColor = new Color(0.26f, 0.52f, 0.96f, 0.15f); + public static readonly Color HoverColor = new Color(0.0f, 0.0f, 0.0f, 0.05f); + + // Dimensions + public static class Dimensions + { + public const int Padding = 16; + public const int PaddingSmall = 8; + public const int PaddingLarge = 24; + public const int CornerRadius = 8; + public const int ButtonHeight = 48; + public const int CardElevation = 4; + } + + // Font Sizes + public static class FontSizes + { + public const float Title = 24.0f; + public const float Subtitle = 18.0f; + public const float Body = 16.0f; + public const float Caption = 14.0f; + public const float Small = 12.0f; + } + + // Helper Methods + public static Shadow CreateShadow() + { + return new Shadow(2.0f, new Color(0, 0, 0, 0.2f), new Vector2(0, 2)); + } + + public static View CreateCard() + { + return new View + { + BackgroundColor = CardBackground, + CornerRadius = Dimensions.CornerRadius, + BoxShadow = new Shadow(Dimensions.CardElevation, new Color(0, 0, 0, 0.1f), new Vector2(0, 2)) + }; + } + + public static Button CreatePrimaryButton(string text) + { + return new Button + { + Text = text, + BackgroundColor = PrimaryBlue, + TextColor = White, + CornerRadius = Dimensions.CornerRadius / 2, + HeightSpecification = Dimensions.ButtonHeight, + PointSize = FontSizes.Caption, + }; + } + + public static Button CreateSecondaryButton(string text, Color color) + { + return new Button + { + Text = text, + BackgroundColor = color, + TextColor = White, + CornerRadius = Dimensions.CornerRadius / 2, + HeightSpecification = Dimensions.ButtonHeight, + PointSize = FontSizes.Body + }; + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/ViewModels/MainViewModel.cs b/IoT/UI/ApplicationControl/ApplicationControl/ViewModels/MainViewModel.cs new file mode 100755 index 000000000..a08c095fb --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/ViewModels/MainViewModel.cs @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 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.ObjectModel; +using System.ComponentModel; +using Tizen.NUI; +using ApplicationControl.Models; +using ApplicationControl.Helpers; + +namespace ApplicationControl.ViewModels +{ + /// + /// A class for main view model + /// + public class MainViewModel : INotifyPropertyChanged + { + ObservableCollection _applications; + Message _message; + AppControlType _selectedAppControlType; + ApplicationListItem _selectedItem; + + /// + /// A constructor for the MainViewModel class + /// + public MainViewModel() + { + Initialize(); + } + + /// + /// To execute the selected application + /// + public void ExecuteSelectedApplication() + { + if (SelectedItem == null || SelectedItem?.Id == null || SelectedAppControlType == AppControlType.Unknown) + { + return; + } + + Console.WriteLine($"Execute id:{SelectedItem.Id}, type:{SelectedAppControlType}"); + ApplicationControlHelper.ExecuteApplication(SelectedAppControlType, SelectedItem.Id); + } + + /// + /// To kill the selected application + /// + public void KillSelectedApplication() + { + if (SelectedItem == null || SelectedItem?.Id == null) + { + return; + } + + ApplicationControlHelper.KillApplication(SelectedItem.Id); + } + + /// + /// To send a message + /// + public void SendMessage() + { + ApplicationControlHelper.ExecuteApplication(AppControlType.Send, null, Message); + } + + /// + /// The selected application control type + /// + public AppControlType SelectedAppControlType + { + get + { + return _selectedAppControlType; + } + + set + { + if (_selectedAppControlType == value) + { + return; + } + + _selectedAppControlType = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedAppControlType")); + } + } + + /// + /// Applications information for a specific application control + /// + public ObservableCollection Applications + { + get + { + return _applications; + } + } + + /// + /// The selected application item on the application list + /// + public ApplicationListItem SelectedItem + { + get + { + return _selectedItem; + } + + set + { + if (_selectedItem == value) + { + return; + } + + if (_selectedItem != null) + { + _selectedItem.BlendColor = Resources.Gray; + } + + _selectedItem = value; + if (_selectedItem != null) + { + _selectedItem.BlendColor = Resources.Transparent; + } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedItem")); + } + } + + /// + /// A Message to send + /// + public Message Message + { + get + { + return _message; + } + } + + /// + /// To initialize some fields when the class is instantiated + /// + void Initialize() + { + _applications = new ObservableCollection(); + _message = new Message { }; + _selectedAppControlType = AppControlType.Unknown; + _selectedItem = new ApplicationListItem { Id = null }; + } + + public event PropertyChangedEventHandler PropertyChanged; + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Views/ApplicationLayout.cs b/IoT/UI/ApplicationControl/ApplicationControl/Views/ApplicationLayout.cs new file mode 100755 index 000000000..e0d8434e6 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Views/ApplicationLayout.cs @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2017 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.ComponentModel; +using System.Threading.Tasks; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using ApplicationControl.ViewModels; +using ApplicationControl.Helpers; +using ApplicationControl.Models; + +namespace ApplicationControl.Views +{ + /// + /// A class for an application list layout as a part of the main layout + /// + public class ApplicationLayout : View + { + private MainViewModel _viewModel; + private ScrollableBase _scrollView; + private View _applicationsContainer; + private TextLabel _emptyStateLabel; + + public ApplicationLayout(MainViewModel viewModel, int screenWidth, int screenHeight) + { + _viewModel = viewModel; + + BackgroundColor = Resources.CardBackground; + CornerRadius = Resources.Dimensions.CornerRadius; + BoxShadow = new Shadow(Resources.Dimensions.CardElevation, new Color(0, 0, 0, 0.1f), new Vector2(0, 2)); + + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, Resources.Dimensions.Padding) + }; + + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding); + + SetPropertyChangeListener(); + InitializeComponent(); + } + + void InitializeComponent() + { + // Header + var header = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + VerticalAlignment = VerticalAlignment.Center, + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + var title = new TextLabel + { + Text = "Applications", + PointSize = Resources.FontSizes.Title, + TextColor = Resources.TextPrimary, + FontStyle = new PropertyMap().Add("weight", new PropertyValue("bold")), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Weight = 1.0f + }; + header.Add(title); + Add(header); + + // Scrollable container for applications + _scrollView = new ScrollableBase + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 1.0f, + ScrollingDirection = ScrollableBase.Direction.Vertical, + HideScrollbar = false + }; + + _applicationsContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, Resources.Dimensions.PaddingSmall) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + _scrollView.Add(_applicationsContainer); + Add(_scrollView); + + // Empty state + _emptyStateLabel = new TextLabel + { + Text = "Select an operation type to view applications", + PointSize = Resources.FontSizes.Body, + TextColor = Resources.TextHint, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + MultiLine = true + }; + _applicationsContainer.Add(_emptyStateLabel); + + // Action buttons + var buttonContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + CellPadding = new Size2D(Resources.Dimensions.Padding, 0) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + var executeButton = Resources.CreateSecondaryButton("Execute", Resources.AccentGreen); + executeButton.Weight = 1.0f; + executeButton.Clicked += (s, e) => + { + _viewModel.ExecuteSelectedApplication(); + }; + + var killButton = Resources.CreateSecondaryButton("Kill", Resources.AccentRed); + killButton.Weight = 1.0f; + killButton.Clicked += (s, e) => + { + _viewModel.KillSelectedApplication(); + }; + + buttonContainer.Add(executeButton); + buttonContainer.Add(killButton); + Add(buttonContainer); + } + + void SetPropertyChangeListener() + { + _viewModel.PropertyChanged += OnPropertyChanged; + } + + async void OnPropertyChanged(object s, PropertyChangedEventArgs e) + { + if (e.PropertyName == "SelectedAppControlType") + { + _viewModel.Applications.Clear(); + await UpdateContentLayout(); + } + } + + Task UpdateContentLayout() + { + return Task.Run(() => + { + // Clear existing items + _applicationsContainer.Children.Clear(); + + var selected = _viewModel.SelectedAppControlType; + if (selected == AppControlType.Unknown) + { + // Show empty state + _applicationsContainer.Add(_emptyStateLabel); + return; + } + + var apps = ApplicationControlHelper.GetApplicationIdsForSpecificAppControlType(selected); + + foreach (var app in apps) + { + var iconPath = ApplicationControlHelper.GetApplicationIconPath(app); + + var appItem = new ApplicationListItem + { + Id = app, + IconPath = iconPath, + BlendColor = Resources.Gray, + }; + _viewModel.Applications.Add(appItem); + + var appLayoutItem = CreateApplicationLayoutItem(appItem); + _applicationsContainer.Add(appLayoutItem); + } + }); + } + + View CreateApplicationLayoutItem(ApplicationListItem item) + { + var container = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + LinearAlignment = LinearLayout.Alignment.CenterVertical, + CellPadding = new Size2D(Resources.Dimensions.Padding, 0) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = 72, + BackgroundColor = Resources.CardBackground, + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.PaddingSmall, (ushort)Resources.Dimensions.PaddingSmall) + }; + + // Icon + View iconContainer; + if (!string.IsNullOrEmpty(item.IconPath)) + { + iconContainer = new ImageView + { + ResourceUrl = item.IconPath, + WidthSpecification = 48, + HeightSpecification = 48, + CornerRadius = 24 + }; + } + else + { + // Placeholder icon + iconContainer = new View + { + BackgroundColor = Resources.LightGray, + WidthSpecification = 48, + HeightSpecification = 48, + CornerRadius = 24 + }; + } + + // Text container + var textContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, 2) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Weight = 1.0f + }; + + var appIdLabel = new TextLabel + { + Text = item.Id ?? "Unknown", + PointSize = Resources.FontSizes.Body, + TextColor = Resources.TextPrimary, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Ellipsis = true + }; + + textContainer.Add(appIdLabel); + + container.Add(iconContainer); + container.Add(textContainer); + + // Selection indicator + var indicator = new View + { + BackgroundColor = Resources.Transparent, + WidthSpecification = 4, + HeightSpecification = LayoutParamPolicies.MatchParent, + CornerRadius = 2 + }; + container.Add(indicator); + + // Touch handling + container.TouchEvent += (sender, e) => + { + if (e.Touch.GetState(0) == PointStateType.Down) + { + container.BackgroundColor = Resources.HoverColor; + } + else if (e.Touch.GetState(0) == PointStateType.Up) + { + container.BackgroundColor = Resources.CardBackground; + + // Update selection + foreach (var app in _viewModel.Applications) + { + if (app.Id == item.Id) + { + _viewModel.SelectedItem = app; + indicator.BackgroundColor = Resources.PrimaryBlue; + } + } + } + return false; + }; + + return container; + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Views/ComposeLayout.cs b/IoT/UI/ApplicationControl/ApplicationControl/Views/ComposeLayout.cs new file mode 100755 index 000000000..abff76fe7 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Views/ComposeLayout.cs @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using ApplicationControl.ViewModels; + +namespace ApplicationControl.Views +{ + /// + /// A class for a compose layout as a part of the main layout + /// + public class ComposeLayout : View + { + private MainViewModel _viewModel; + + public ComposeLayout(MainViewModel viewModel) + { + _viewModel = viewModel; + + BackgroundColor = Resources.CardBackground; + CornerRadius = Resources.Dimensions.CornerRadius; + BoxShadow = new Shadow(Resources.Dimensions.CardElevation, new Color(0, 0, 0, 0.1f), new Vector2(0, 2)); + + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, Resources.Dimensions.Padding) + }; + + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding); + + InitializeComponent(); + } + + void InitializeComponent() + { + // Header + var title = new TextLabel + { + Text = "Compose Message", + PointSize = Resources.FontSizes.Title, + TextColor = Resources.TextPrimary, + FontStyle = new PropertyMap().Add("weight", new PropertyValue("bold")), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + Add(title); + + // Message preview + var messageContainer = new View + { + BackgroundColor = Resources.SectionBackground, + CornerRadius = Resources.Dimensions.CornerRadius / 2, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Weight = 1.0f, + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding) + }; + + var messageLabel = new TextLabel + { + Text = _viewModel.Message.Text, + TextColor = Resources.TextSecondary, + PointSize = Resources.FontSizes.Caption, + MultiLine = true, + LineWrapMode = LineWrapMode.Word, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent + }; + messageContainer.Add(messageLabel); + Add(messageContainer); + + // Input section + var inputContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + CellPadding = new Size2D(Resources.Dimensions.Padding, 0) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + var addressEntry = new TextField + { + PlaceholderText = "Recipient email address", + BackgroundColor = Resources.LightGray, + TextColor = Resources.TextPrimary, + PlaceholderTextColor = Resources.TextHint, + PointSize = Resources.FontSizes.Body, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = Resources.Dimensions.ButtonHeight, + Weight = 1.0f, + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, 0, 0), + CornerRadius = Resources.Dimensions.CornerRadius / 2 + }; + + addressEntry.TextChanged += (s, e) => + { + _viewModel.Message.To = e.TextField.Text; + }; + + var sendButton = Resources.CreatePrimaryButton("Send"); + sendButton.WidthSpecification = 120; + sendButton.Clicked += (s, e) => + { + _viewModel.SendMessage(); + }; + + inputContainer.Add(addressEntry); + inputContainer.Add(sendButton); + Add(inputContainer); + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/Views/OperationLayout.cs b/IoT/UI/ApplicationControl/ApplicationControl/Views/OperationLayout.cs new file mode 100755 index 000000000..d9b278c45 --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/Views/OperationLayout.cs @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 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 Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using ApplicationControl.ViewModels; +using ApplicationControl.Helpers; + +namespace ApplicationControl.Views +{ + /// + /// A class for an operation layout as a part of the main layout + /// + public class OperationLayout : View + { + private MainViewModel _viewModel; + private RadioButtonGroup _radioGroup; + + public OperationLayout(MainViewModel viewModel) + { + _viewModel = viewModel; + BackgroundColor = Resources.CardBackground; + CornerRadius = Resources.Dimensions.CornerRadius; + BoxShadow = new Shadow(Resources.Dimensions.CardElevation, new Color(0, 0, 0, 0.1f), new Vector2(0, 2)); + + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, Resources.Dimensions.PaddingSmall) + }; + + Padding = new Extents((ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding, + (ushort)Resources.Dimensions.Padding, (ushort)Resources.Dimensions.Padding); + + // Create RadioButtonGroup for mutual exclusion + _radioGroup = new RadioButtonGroup(); + + InitializeComponent(); + } + + void InitializeComponent() + { + // Title + var title = new TextLabel + { + Text = "Operation Type", + PointSize = Resources.FontSizes.Title, + TextColor = Resources.TextPrimary, + FontStyle = new PropertyMap().Add("weight", new PropertyValue("bold")), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Margin = new Extents(0, 0, 0, (ushort)Resources.Dimensions.Padding) + }; + Add(title); + + // Subtitle + var subtitle = new TextLabel + { + Text = "Select an operation to view matching applications", + PointSize = Resources.FontSizes.Caption, + TextColor = Resources.TextSecondary, + MultiLine = true, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Margin = new Extents(0, 0, 0, (ushort)Resources.Dimensions.PaddingLarge) + }; + Add(subtitle); + + // Create operation items + Add(CreateOperationItem("View", "Open and view content", AppControlType.View, Resources.PrimaryBlue)); + Add(CreateOperationItem("Pick", "Select files or data", AppControlType.Pick, Resources.AccentOrange)); + Add(CreateOperationItem("Compose", "Create new content", AppControlType.Compose, Resources.AccentGreen)); + } + + View CreateOperationItem(string title, string description, AppControlType type, Color accentColor) + { + var container = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + LinearAlignment = LinearLayout.Alignment.CenterVertical, + CellPadding = new Size2D(Resources.Dimensions.Padding, 0) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Margin = new Extents(0, 0, 0, (ushort)Resources.Dimensions.PaddingSmall) + }; + + // Radio Button + var radioButton = new RadioButton + { + IsSelected = false, + WidthSpecification = 24, + HeightSpecification = 24 + }; + + // Add to RadioButtonGroup for mutual exclusion + _radioGroup.Add(radioButton); + + radioButton.SelectedChanged += (s, e) => + { + if (e.IsSelected == false) + { + return; + } + + // Update selected item if type changes + if (type != _viewModel.SelectedAppControlType) + { + if (_viewModel.SelectedItem != null) + { + _viewModel.SelectedItem.Id = null; + } + } + + _viewModel.SelectedAppControlType = type; + }; + + // Text container + var textContainer = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + CellPadding = new Size2D(0, 4) + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Weight = 1.0f + }; + + // Title + var titleLabel = new TextLabel + { + Text = title, + PointSize = Resources.FontSizes.Body, + TextColor = Resources.TextPrimary, + FontStyle = new PropertyMap().Add("weight", new PropertyValue("medium")), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + // Description + var descLabel = new TextLabel + { + Text = description, + PointSize = Resources.FontSizes.Small, + TextColor = Resources.TextSecondary, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + + textContainer.Add(titleLabel); + textContainer.Add(descLabel); + + // Accent indicator + var indicator = new View + { + BackgroundColor = accentColor, + WidthSpecification = 4, + HeightSpecification = LayoutParamPolicies.MatchParent, + CornerRadius = 2 + }; + + container.Add(radioButton); + container.Add(textContainer); + container.Add(indicator); + + // Add touch feedback + container.TouchEvent += (sender, e) => + { + if (e.Touch.GetState(0) == PointStateType.Down) + { + container.BackgroundColor = Resources.HoverColor; + } + else if (e.Touch.GetState(0) == PointStateType.Up) + { + container.BackgroundColor = Resources.Transparent; + radioButton.IsSelected = true; + } + return false; + }; + + return container; + } + } +} diff --git a/IoT/UI/ApplicationControl/ApplicationControl/shared/res/ApplicationControl.png b/IoT/UI/ApplicationControl/ApplicationControl/shared/res/ApplicationControl.png new file mode 100755 index 000000000..9f3cb9860 Binary files /dev/null and b/IoT/UI/ApplicationControl/ApplicationControl/shared/res/ApplicationControl.png differ diff --git a/IoT/UI/ApplicationControl/ApplicationControl/tizen-manifest.xml b/IoT/UI/ApplicationControl/ApplicationControl/tizen-manifest.xml new file mode 100755 index 000000000..2ed4429ed --- /dev/null +++ b/IoT/UI/ApplicationControl/ApplicationControl/tizen-manifest.xml @@ -0,0 +1,22 @@ + + + + + + ApplicationControl.png + + + + http://tizen.org/privilege/appmanager.launch + http://tizen.org/privilege/appmanager.kill + http://tizen.org/privilege/packagemanager.info + +