From: Shivam Varshney/Core S/W Group /SRI-Delhi/Engineer/Samsung Electronics Date: Fri, 22 Jul 2022 06:34:58 +0000 (+0530) Subject: Initial Code for Tray Application X-Git-Tag: submit/tizen/20220725.062700~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=044d4f85ec4ed7e01e5fdd863aa98d7eaa795f43;p=profile%2Fiot%2Fapps%2Fdotnet%2Ftray.git Initial Code for Tray Application Change-Id: Ic5acc1f79164dbde04e79c3db6bed193635ac25b Signed-off-by: Shivam Varshney/Core S/W Group /SRI-Delhi/Engineer/Samsung Electronics --- diff --git a/.gitignore b/.gitignore index 010c61f..5af97cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +TrayApplication/.vs/ +TrayApplication/bin/ +TrayApplication/obj/ +TrayApplication/TrayApplication.csproj.user Apps/.vs/ Apps/bin/ Apps/obj/ diff --git a/TrayApplication/Common/DeviceInfo.cs b/TrayApplication/Common/DeviceInfo.cs new file mode 100644 index 0000000..248a18a --- /dev/null +++ b/TrayApplication/Common/DeviceInfo.cs @@ -0,0 +1,31 @@ +using Tizen.System; + +namespace TrayApplication.Common +{ + public static class DeviceInfo + { + private static int width; + private static int height; + private const string WidthKey = "tizen.org/feature/screen.width"; + private const string HeightKey = "tizen.org/feature/screen.height"; + + static DeviceInfo() + { + bool isWidthAvailable = Information.TryGetValue(WidthKey, out width); + bool isHeightAvailable = Information.TryGetValue(HeightKey, out height); + if (isHeightAvailable == false || isWidthAvailable == false) + { + width = 1920; + height = 1080; + Tizen.Log.Debug(Resources.LogTag, "Width and height are not available , setting default size as 1920 x 1080"); + } + IsPortrait = width < height; + } + + public static bool IsPortrait { get; private set; } + + public static int DisplayWidth => width; + + public static int DisplayHeight => height; + } +} diff --git a/TrayApplication/Common/PropertyNotifier.cs b/TrayApplication/Common/PropertyNotifier.cs new file mode 100644 index 0000000..2794eaa --- /dev/null +++ b/TrayApplication/Common/PropertyNotifier.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace TrayApplication.Common +{ + class PropertyNotifier : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (Equals(storage, value)) + { + return false; + } + + storage = value; + OnPropertyChanged(propertyName); + return true; + } + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/TrayApplication/Common/Resources.cs b/TrayApplication/Common/Resources.cs new file mode 100644 index 0000000..2071129 --- /dev/null +++ b/TrayApplication/Common/Resources.cs @@ -0,0 +1,40 @@ +using Tizen.NUI; + +namespace TrayApplication.Common +{ + class Resources + { + public const string LogTag = "Tray_Application"; + public const string LightPlatformThemeId = "org.tizen.default-light-theme"; + public const string DarkPlatformThemeId = "org.tizen.default-dark-theme"; + public static readonly Color LightApplicationsBackground = new Color(1.0f, 1.0f, 1.0f, 0.3f); + public static readonly Color LightQuickAccessBackground = new Color(1.0f, 1.0f, 1.0f, 0.5f); + public static readonly Color DarkApplicationsBackground = new Color(0.0863f, 0.0745f, 0.098f, 0.3f); + public static readonly Color DarkQuickAccessBackground = new Color(0.0863f, 0.0745f, 0.098f, 0.5f); + + public static string GetImagePath() + { + return Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/"; + } + + public static string GetLightImagePath() + { + return GetImagePath() + "light/"; + } + + public static string GetDarkImagePath() + { + return GetImagePath() + "dark/"; + } + + public static string GetThemePath() + { + return Tizen.Applications.Application.Current.DirectoryInfo.Resource + "themes/"; + } + + public static string GetCurrentThemePath() + { + return (ThemeManager.PlatformThemeId == LightPlatformThemeId) ? GetLightImagePath() : GetDarkImagePath(); + } + } +} diff --git a/TrayApplication/Core/AppLauncher.cs b/TrayApplication/Core/AppLauncher.cs new file mode 100644 index 0000000..3b88850 --- /dev/null +++ b/TrayApplication/Core/AppLauncher.cs @@ -0,0 +1,17 @@ +using Tizen.Applications; + +namespace TrayApplication.Core +{ + static class AppLauncher + { + public static void LaunchApplication(string id) + { + AppControl appControl = new AppControl() + { + ApplicationId = id, + Operation = id == "org.tizen.homescreen-efl" ? AppControlOperations.Main : AppControlOperations.Default, + }; + AppControl.SendLaunchRequest(appControl); + } + } +} diff --git a/TrayApplication/Core/AppScoreDataBase.cs b/TrayApplication/Core/AppScoreDataBase.cs new file mode 100644 index 0000000..b54ea09 --- /dev/null +++ b/TrayApplication/Core/AppScoreDataBase.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using Microsoft.Data.Sqlite; +using Tizen.Applications; +using TrayApplication.Common; + +namespace TrayApplication.Core +{ + static class AppScoreDataBase + { + private const string CREATE_TABLE_QUERY = "CREATE TABLE APP_INFO(NAME VARCHAR(30), SCORE FLOAT(10));"; + private const string INSERT_VALUES_QUERY = "INSERT INTO APP_INFO(NAME, SCORE) VALUES('"; + private const string SELECT_MAX_SCORE_QUERY = "SELECT MAX(SCORE) FROM APP_INFO;"; + private const string UPDATE_LAUNCHED_APP_QUERY = "UPDATE APP_INFO SET SCORE = SCORE + 50.00 WHERE NAME = '"; + private const string UPDATE_OTHER_APPS_QUERY = "UPDATE APP_INFO SET SCORE = SCORE * 0.90 WHERE NAME !='"; + private const string DELETE_APP_QUERY = "DELETE FROM APP_INFO WHERE NAME = '"; + private const string SELECT_APPS_QUERY = "SELECT * FROM APP_INFO ORDER BY SCORE DESC LIMIT "; + private static SqliteConnection sqliteConn; + + public static event EventHandler OnDatabaseUpdate; + + public static void InitializeDataBase() + { + Connect(); + CreateTable(); + PackageManager.InstallProgressChanged += OnInstallProgressChanged; + PackageManager.UninstallProgressChanged += OnUninstallProgressChanged; + ApplicationManager.ApplicationLaunched += OnApplicationLaunched; + } + + private static void OnInstallProgressChanged(object sender, PackageManagerEventArgs e) + { + if (e.State == PackageEventState.Completed) + { + Package package = PackageManager.GetPackage(e.PackageId); + foreach (ApplicationInfo appInfo in package.GetApplications()) + { + Tizen.Log.Info(Resources.LogTag, "Updating " + appInfo.ApplicationId); + if (appInfo.Label == "TrayApplication" || appInfo.Label == "Apps" || string.IsNullOrEmpty(appInfo.IconPath) || appInfo.IsNoDisplay == true) + { + continue; + } + IncreaseScore(appInfo.ApplicationId, true); + OnDatabaseUpdate.Invoke(null, new EventArgs()); + } + } + } + + private static void OnUninstallProgressChanged(object sender, PackageManagerEventArgs e) + { + if (e.Progress ==0) + { + Package package = PackageManager.GetPackage(e.PackageId); + foreach (ApplicationInfo appInfo in package.GetApplications()) + { + Tizen.Log.Info(Resources.LogTag, "Updating " + appInfo.ApplicationId); + if (appInfo.Label == "TrayApplication" || appInfo.Label == "Apps" || string.IsNullOrEmpty(appInfo.IconPath) || appInfo.IsNoDisplay == true) + { + continue; + } + RemoveAppScore(appInfo.ApplicationId); + OnDatabaseUpdate.Invoke(null, new EventArgs()); + } + } + } + + private static void OnApplicationLaunched(object sender, ApplicationLaunchedEventArgs e) + { + ApplicationInfo appInfo = ApplicationManager.GetInstalledApplication(e.ApplicationRunningContext.ApplicationId); + if (appInfo.Label == "TrayApplication" || appInfo.Label == "Apps" || string.IsNullOrEmpty(appInfo.IconPath) || appInfo.IsNoDisplay == true) + { + return; + } + IncreaseScore(e.ApplicationRunningContext.ApplicationId, false); + OnDatabaseUpdate.Invoke(null, new EventArgs()); + } + + private static void Connect() + { + string path = "Data Source = " + Application.Current.DirectoryInfo.Data + "database.db"; + Tizen.Log.Info(Resources.LogTag, "path = " + path); + try + { + sqliteConn = new SqliteConnection(path); + } + catch(Exception ex) + { + Tizen.Log.Error(Resources.LogTag, "Error " + ex.Message); + } + try + { + sqliteConn.Open(); + } + catch (Exception ex) + { + Tizen.Log.Error(Resources.LogTag, "Error " + ex.Message); + } + Tizen.Log.Info(Resources.LogTag, "Connection Open"); + } + + private static void CreateTable() + { + SqliteCommand sqliteCmd = sqliteConn.CreateCommand(); + sqliteCmd.CommandText = CREATE_TABLE_QUERY; + try + { + sqliteCmd.ExecuteNonQuery(); + InsertData(); + } + catch (Exception ex) + { + Tizen.Log.Error(Resources.LogTag, "Error " + ex.Message); + } + Tizen.Log.Info(Resources.LogTag, "Create Table Done"); + } + private static void InsertData() + { + SqliteCommand sqliteCmd = sqliteConn.CreateCommand(); + ApplicationInfoFilter appInfoFilter = new ApplicationInfoFilter(); + appInfoFilter.Filter.Add(ApplicationInfoFilter.Keys.NoDisplay, "False"); + var task = ApplicationManager.GetInstalledApplicationsAsync(appInfoFilter); + task.Wait(); + IEnumerable applicationsList = task.Result; + foreach( ApplicationInfo appInfo in applicationsList) + { + if (appInfo.Label == "TrayApplication" || appInfo.Label == "Apps" || string.IsNullOrEmpty(appInfo.IconPath)) + { + continue; + } + sqliteCmd.CommandText = INSERT_VALUES_QUERY + appInfo.ApplicationId + "', 50.00); "; + sqliteCmd.ExecuteNonQuery(); + } + } + + private static void IncreaseScore(string appId, bool isInstalled) + { + SqliteCommand sqliteCmd = sqliteConn.CreateCommand(); + + if (isInstalled) + { + sqliteCmd.CommandText = SELECT_MAX_SCORE_QUERY; + SqliteDataReader sqliteDatareader = sqliteCmd.ExecuteReader(); + sqliteDatareader.Read(); + float score = sqliteDatareader.GetFloat(0); + sqliteCmd.Dispose(); + sqliteCmd = sqliteConn.CreateCommand(); + sqliteCmd.CommandText = INSERT_VALUES_QUERY + appId + "', " + score + ");"; + sqliteCmd.ExecuteNonQuery(); + } + else + { + sqliteCmd.CommandText = UPDATE_LAUNCHED_APP_QUERY + appId + "';"; + sqliteCmd.ExecuteNonQuery(); + } + + sqliteCmd.CommandText = UPDATE_OTHER_APPS_QUERY + appId + "';"; + sqliteCmd.ExecuteNonQuery(); + Tizen.Log.Info(Resources.LogTag, "Score Updated of App: " + appId); + } + + private static void RemoveAppScore(string appId) + { + SqliteCommand sqliteCmd = sqliteConn.CreateCommand(); + sqliteCmd.CommandText = DELETE_APP_QUERY + appId + "';"; + sqliteCmd.ExecuteNonQuery(); + Tizen.Log.Info(Resources.LogTag, "App Removed from DB: " + appId); + } + + public static Dictionary ReadData(int count) + { + SqliteDataReader sqliteDatareader; + SqliteCommand sqliteCmd = sqliteConn.CreateCommand(); + sqliteCmd.CommandText = SELECT_APPS_QUERY + count + ";"; + Dictionary appsScoreData = new Dictionary(); + sqliteDatareader = sqliteCmd.ExecuteReader(); + while (sqliteDatareader.Read()) + { + string appName = sqliteDatareader.GetString(0); + int appScore = sqliteDatareader.GetInt32(1); + Tizen.Log.Info(Resources.LogTag, "appName " + appName + " score " + appScore); + appsScoreData.Add(appName, appScore); + } + return appsScoreData; + } + + public static void Disconnect() + { + sqliteConn.Close(); + } + } +} diff --git a/TrayApplication/Models/AppInfoModel.cs b/TrayApplication/Models/AppInfoModel.cs new file mode 100644 index 0000000..2c96be2 --- /dev/null +++ b/TrayApplication/Models/AppInfoModel.cs @@ -0,0 +1,41 @@ +using System.Windows.Input; +using Tizen.NUI.Binding; +using TrayApplication.Common; +using TrayApplication.Core; + +namespace TrayApplication.Models +{ + class AppInfoModel : PropertyNotifier + { + public AppInfoModel(string name, string applicationId, string url) + { + Name = name; + ApplicationId = applicationId; + IconUrl = url; + AppSelectCommand = new Command(OnAppSelect); + } + + public string Name { get; internal set; } + + public string ApplicationId { get; internal set; } + + private string iconUrl; + public string IconUrl + { + get => iconUrl; + set => SetProperty(ref iconUrl, value); + } + + private ICommand appSelectCommand; + public ICommand AppSelectCommand + { + get => appSelectCommand; + set => SetProperty(ref appSelectCommand, value); + } + + private void OnAppSelect(object selectedItem) + { + AppLauncher.LaunchApplication(ApplicationId); + } + } +} diff --git a/TrayApplication/TrayApplication.cs b/TrayApplication/TrayApplication.cs new file mode 100644 index 0000000..dd2896b --- /dev/null +++ b/TrayApplication/TrayApplication.cs @@ -0,0 +1,148 @@ +using System; +using Tizen.NUI; +using Tizen.NUI.WindowSystem.Shell; +using TrayApplication.Views; +using TrayApplication.Common; +using TrayApplication.Core; + +namespace TrayApplication +{ + public class Program : NUIApplication + { + public Program() : base(ThemeOptions.PlatformThemeEnabled) + { + } + + private const int WindowHeight = 313; + private int positionX; + private int positionY; + private bool isTrayVisible = true; + + private Window window; + private MainView mainView; + private TizenShell tzShell; + private SoftkeyService softkeyService; + + private float touchStartPosition; + protected override void OnCreate() + { + Tizen.Log.Info(Resources.LogTag, "Program OnCreate"); + base.OnCreate(); + window = GetDefaultWindow(); + int sizeWidth = (int)((DeviceInfo.IsPortrait ? 0.6 : 0.5) * DeviceInfo.DisplayWidth); + int sizeHeight = WindowHeight.SpToPx(); + positionX = (int)((DeviceInfo.IsPortrait ? 0.2 : 0.25) * DeviceInfo.DisplayWidth); + positionY = DeviceInfo.DisplayHeight; + window.WindowSize = new Size2D(sizeWidth, sizeHeight); + window.WindowPosition = new Position2D(positionX, positionY); + window.BackgroundColor = Color.Transparent; + window.SetTransparency(true); + + tzShell = new TizenShell(); + softkeyService = new SoftkeyService(tzShell, window); + softkeyService.Show(); + + window.KeyEvent += OnKeyEvent; + window.TouchEvent += OnTouch; + AppScoreDataBase.InitializeDataBase(); + + mainView = new MainView(); + window.Add(mainView); + mainView.RemovedFromWindow += MainViewRemovedFromWindow; + mainView.VisibilityChanged += MainViewVisibilityChanged; + mainView.HideView(); + Tizen.Log.Info(Resources.LogTag, "Tray Application Created"); + } + + protected override void OnTerminate() + { + Tizen.Log.Info(Resources.LogTag, "Program OnTerminate"); + AppScoreDataBase.Disconnect(); + base.OnTerminate(); + } + + private void MainViewRemovedFromWindow(object sender, EventArgs e) + { + Tizen.Log.Info(Resources.LogTag, "Main View Removed"); + mainView = null; + Exit(); + } + + private void MainViewVisibilityChanged(object sender, Tizen.NUI.BaseComponents.View.VisibilityChangedEventArgs e) + { + Tizen.Log.Info(Resources.LogTag, "Main View Visibility Changed"); + if (isTrayVisible == true) + { + positionY = DeviceInfo.DisplayHeight - 36.SpToPx(); + window.WindowPosition = new Position2D(positionX, positionY); + } + else + { + positionY = DeviceInfo.DisplayHeight - (WindowHeight + 48).SpToPx(); + window.WindowPosition = new Position2D(positionX, positionY); + } + isTrayVisible = !isTrayVisible; + } + + private void OnTouch(object sender, Window.TouchEventArgs e) + { + Tizen.Log.Info(Resources.LogTag, "Touch Type " + e.Touch.GetState(0).ToString()); + if (isTrayVisible == true) + { + if (e.Touch.GetState(0) == PointStateType.Started) + { + touchStartPosition = e.Touch.GetScreenPosition(0).Y; + } + if (e.Touch.GetState(0) == PointStateType.Finished) + { + float touchEndPosition = e.Touch.GetScreenPosition(0).Y; + if (touchEndPosition - touchStartPosition >= 60.SpToPx()) + { + if (mainView != null) + { + mainView.HideView(); + } + } + } + } + else + { + if (e.Touch.GetState(0) == PointStateType.Started) + { + touchStartPosition = e.Touch.GetScreenPosition(0).Y; + } + if (e.Touch.GetState(0) == PointStateType.Motion || e.Touch.GetState(0) == PointStateType.Finished) + { + float touchEndPosition = e.Touch.GetScreenPosition(0).Y; + if (touchStartPosition - touchEndPosition >= 5.SpToPx()) + { + if (mainView != null) + { + mainView.ShowView(); + } + touchStartPosition = 313.SpToPx(); + } + } + } + } + + public void OnKeyEvent(object sender, Window.KeyEventArgs e) + { + if (e.Key.State == Key.StateType.Down && (e.Key.KeyPressedName == "XF86Back" || e.Key.KeyPressedName == "Escape")) + { + if (mainView != null) + { + mainView.HideView(); + } + } + } + + static void Main(string[] args) + { + Tizen.Log.Info(Resources.LogTag, "Main statrted"); + Program app = new Program(); + app.Run(args); + Tizen.Log.Info(Resources.LogTag, "Main ended"); + } + } +} diff --git a/TrayApplication/TrayApplication.csproj b/TrayApplication/TrayApplication.csproj new file mode 100644 index 0000000..b827d0e --- /dev/null +++ b/TrayApplication/TrayApplication.csproj @@ -0,0 +1,36 @@ + + + + Exe + netcoreapp3.1 + Tizen + TrayApplication + + + + portable + + + None + + + + + + + + + + + + + + + MSBuild:Compile + + + MSBuild:Compile + + + + diff --git a/TrayApplication/TrayApplication.sln b/TrayApplication/TrayApplication.sln new file mode 100644 index 0000000..0a80bef --- /dev/null +++ b/TrayApplication/TrayApplication.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrayApplication", "TrayApplication.csproj", "{A49D73AA-CB7E-4D21-B865-E61570EEF070}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A49D73AA-CB7E-4D21-B865-E61570EEF070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A49D73AA-CB7E-4D21-B865-E61570EEF070}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A49D73AA-CB7E-4D21-B865-E61570EEF070}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A49D73AA-CB7E-4D21-B865-E61570EEF070}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BEA88B8F-9C50-44CA-8A31-B3DDC15622F5} + EndGlobalSection +EndGlobal diff --git a/TrayApplication/ViewModels/ApplicationViewModel.cs b/TrayApplication/ViewModels/ApplicationViewModel.cs new file mode 100644 index 0000000..3b24279 --- /dev/null +++ b/TrayApplication/ViewModels/ApplicationViewModel.cs @@ -0,0 +1,81 @@ +using System.Collections; +using System.Collections.Generic; +using Tizen.Applications; +using Tizen.NUI; +using TrayApplication.Common; +using TrayApplication.Core; +using TrayApplication.Models; + +namespace TrayApplication.ViewModels +{ + class ApplicationViewModel : PropertyNotifier + { + Dictionary appScoreData; + private readonly int appsCount; + public ApplicationViewModel(int appsCount) + { + this.appsCount = appsCount; + appScoreData = AppScoreDataBase.ReadData(appsCount - 1); + BackgroundColor = ThemeManager.PlatformThemeId == Resources.DarkPlatformThemeId ? Resources.DarkApplicationsBackground : Resources.LightApplicationsBackground; + AddButtonsInfo(); + AppScoreDataBase.OnDatabaseUpdate += OnDatabaseUpdate; + ThemeManager.ThemeChanged += (object sender, ThemeChangedEventArgs e) => + { + if (e.IsPlatformThemeChanged) + { + Tizen.Log.Info(Resources.LogTag, "Theme Changed: " + e.ThemeId); + UpdateTheme(); + } + }; + Tizen.Log.Info(Resources.LogTag, "ApplicationViewModel"); + } + + private void OnDatabaseUpdate(object sender, System.EventArgs e) + { + UpdateButtonsInfo(); + } + + private void AddButtonsInfo() + { + List buttons = new List(); + foreach (var item in appScoreData) + { + ApplicationInfo appInfo = new ApplicationInfo(item.Key); + buttons.Add(new AppInfoModel(appInfo.Label, item.Key, appInfo.IconPath)); + appInfo.Dispose(); + } + buttons.Add(new AppInfoModel("Apps", "org.tizen.Apps", Resources.GetCurrentThemePath() + "apps.png")); + ButtonsInfo = buttons; + } + + public void UpdateButtonsInfo() + { + appScoreData.Clear(); + appScoreData = AppScoreDataBase.ReadData(appsCount - 1); + ((List)ButtonsInfo).Clear(); + AddButtonsInfo(); + } + + public void UpdateTheme() + { + BackgroundColor = ThemeManager.PlatformThemeId == Resources.DarkPlatformThemeId ? Resources.DarkApplicationsBackground : Resources.LightApplicationsBackground; + ((AppInfoModel)((List)buttonsInfo)[^1]).IconUrl = Resources.GetCurrentThemePath() + "apps.png"; + } + + private Color backgroundColor; + + public Color BackgroundColor + { + get => backgroundColor; + set => SetProperty(ref backgroundColor, value); + } + + private IEnumerable buttonsInfo; + + public IEnumerable ButtonsInfo + { + get => buttonsInfo; + set => SetProperty(ref buttonsInfo, value); + } + } +} diff --git a/TrayApplication/ViewModels/QuickAccessViewModel.cs b/TrayApplication/ViewModels/QuickAccessViewModel.cs new file mode 100644 index 0000000..da4529c --- /dev/null +++ b/TrayApplication/ViewModels/QuickAccessViewModel.cs @@ -0,0 +1,71 @@ +using System.Collections; +using System.Collections.Generic; +using Tizen.NUI; +using TrayApplication.Common; +using TrayApplication.Models; + +namespace TrayApplication.ViewModels +{ + class QuickAccessViewModel : PropertyNotifier + { + private readonly List AppNames = new List() { "home", "settings", "volume", "notifications", "gallery", "power" }; + + public QuickAccessViewModel() + { + ButtonsInfo = new List(); + BackgroundColor = ThemeManager.PlatformThemeId == Resources.DarkPlatformThemeId ? Resources.DarkQuickAccessBackground : Resources.LightQuickAccessBackground; + AddButtonsInfo(); + ThemeManager.ThemeChanged += (object sender, ThemeChangedEventArgs e) => + { + if (e.IsPlatformThemeChanged) + { + Tizen.Log.Info(Resources.LogTag, "Theme Changed: " + e.ThemeId); + UpdateTheme(); + } + }; + Tizen.Log.Info(Resources.LogTag, "QuickAccessViewModel"); + } + + private void AddButtonsInfo() + { + string imagePath = Resources.GetCurrentThemePath(); + ((List)ButtonsInfo).Clear(); + List buttons = new List + { + new AppInfoModel(AppNames[0], "org.tizen.homescreen-efl", imagePath + AppNames[0] + ".png"), + new AppInfoModel(AppNames[1], "org.tizen.setting", imagePath + AppNames[1] + ".png"), + new AppInfoModel(AppNames[2], "org.tizen.volume", imagePath + AppNames[2] + ".png"), + new AppInfoModel(AppNames[3], "org.tizen.quickpanel", imagePath + AppNames[3] + ".png"), + new AppInfoModel(AppNames[5], "org.tizen.powerkey-syspopup", imagePath + AppNames[5] + ".png") + }; + ButtonsInfo = buttons; + Tizen.Log.Info(Resources.LogTag, "Done Adding ButtonsInfo"); + } + + public void UpdateTheme() + { + BackgroundColor = ThemeManager.PlatformThemeId == Resources.DarkPlatformThemeId ? Resources.DarkQuickAccessBackground : Resources.LightQuickAccessBackground; + string imagePath = Resources.GetCurrentThemePath(); + foreach (AppInfoModel item in ButtonsInfo) + { + item.IconUrl = imagePath + item.Name + ".png"; + } + } + + private Color backgroundColor; + + public Color BackgroundColor + { + get => backgroundColor; + set => SetProperty(ref backgroundColor, value); + } + + private IEnumerable buttonsInfo; + + public IEnumerable ButtonsInfo + { + get => buttonsInfo; + set => SetProperty(ref buttonsInfo, value); + } + } +} diff --git a/TrayApplication/Views/AppItemView.cs b/TrayApplication/Views/AppItemView.cs new file mode 100644 index 0000000..745916e --- /dev/null +++ b/TrayApplication/Views/AppItemView.cs @@ -0,0 +1,180 @@ +using System; +using System.Windows.Input; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; +using Tizen.NUI.Components; +using TrayApplication.Common; + +namespace TrayApplication.Views +{ + internal class AppItemView : View + { + public event EventHandler LongPressed; + + private bool isMoved; + private bool isLongPressed; + private Timer timer; + + public static readonly BindableProperty AppSelectCommandProperty = BindableProperty.Create(nameof(AppSelectCommand), typeof(ICommand), typeof(AppItemView), null, propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (AppItemView)bindable; + if (oldValue != newValue) + { + instance.appSelectCommand = (ICommand)newValue; + } + }, + defaultValueCreator: (bindable) => ((AppItemView)bindable).appSelectCommand); + + public AppItemView() : base() + { + ThemeChangeSensitive = true; + StyleName = "AppItemBackGround"; + WidthSpecification = 138.SpToPx(); + HeightSpecification = 138.SpToPx(); + CornerRadius = new Vector4(12, 12, 12, 12); + Layout = new RelativeLayout() + { + Padding = new Extents(8, 0, 0, 2).SpToPx(), + }; + + IconBackground = new View() + { + WidthSpecification = 122.SpToPx(), + HeightSpecification = 102.SpToPx(), + CornerRadius = new Vector4(12, 12, 12, 12), + Margin = new Extents(0, 8, 8, 0).SpToPx(), + Layout = new LinearLayout() + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + }, + }; + Add(IconBackground); + RelativeLayout.SetHorizontalAlignment(IconBackground, RelativeLayout.Alignment.Center); + RelativeLayout.SetVerticalAlignment(IconBackground, RelativeLayout.Alignment.Start); + + Icon = new ImageView() + { + MaximumSize = new Size2D(96, 96).SpToPx(), + }; + IconBackground.Add(Icon); + + Label = new TextLabel() + { + ThemeChangeSensitive = true, + StyleName = "AppItemLabel", + FontFamily = "BreezeSans", + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = 24.SpToPx(), + PixelSize = 16.SpToPx(), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + }; + Add(Label); + RelativeLayout.SetHorizontalAlignment(Label, RelativeLayout.Alignment.Center); + RelativeLayout.SetVerticalAlignment(Label, RelativeLayout.Alignment.End); + + TouchEvent += OnTouched; + Tizen.Log.Info(Resources.LogTag, "AppItemView"); + } + + private bool OnTouched(object source, TouchEventArgs e) + { + if (e.Touch.GetState(0) == PointStateType.Down) + { + timer = new Timer(1000); + timer.Start(); + isMoved = false; + isLongPressed = false; + timer.Tick += (object source, Timer.TickEventArgs ev) => + { + if (isMoved == false) + { + isLongPressed = true; + LongPressed.Invoke(this, new EventArgs()); + Tizen.Log.Debug(Resources.LogTag, "Long Pressed"); + } + return false; + }; + } + else + { + if (timer != null) + { + timer.Stop(); + timer.Dispose(); + timer = null; + } + if (e.Touch.GetState(0) == PointStateType.Up) + { + if (isLongPressed == false && isMoved == false) + { + AppSelectCommand.Execute(BindingContext); + Tizen.Log.Debug(Resources.LogTag, "Clicked"); + } + isLongPressed = false; + } + else + { + isMoved = true; + } + } + return true; + } + + public TextLabel Label { get; } + + public ImageView Icon { get; } + + public View IconBackground { get; } + + public Button CrossButton { get; internal set; } + + private ICommand appSelectCommand; + + public ICommand AppSelectCommand + { + get => (ICommand)GetValue(AppSelectCommandProperty); + set => SetValue(AppSelectCommandProperty, value); + } + + public void AddCrossButton() + { + if (CrossButton == null) + { + CrossButton = new Button("CrossButton") + { + Size2D = new Size2D(48, 48).SpToPx(), + }; + Add(CrossButton); + RelativeLayout.SetHorizontalAlignment(CrossButton, RelativeLayout.Alignment.End); + RelativeLayout.SetVerticalAlignment(CrossButton, RelativeLayout.Alignment.Start); + } + } + + protected override void Dispose(DisposeTypes type) + { + if (Disposed) + { + return; + } + if (type == DisposeTypes.Explicit) + { + Remove(Label); + Label?.Dispose(); + + IconBackground.Remove(Icon); + Icon?.Dispose(); + + Remove(IconBackground); + IconBackground?.Dispose(); + + Remove(CrossButton); + CrossButton?.Dispose(); + } + Tizen.Log.Info(Resources.LogTag, "AppItemView Dispose"); + base.Dispose(type); + } + } +} diff --git a/TrayApplication/Views/ApplicationsView.cs b/TrayApplication/Views/ApplicationsView.cs new file mode 100644 index 0000000..630867f --- /dev/null +++ b/TrayApplication/Views/ApplicationsView.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; +using TrayApplication.Common; + +namespace TrayApplication.Views +{ + public class ApplicationsView : View + { + private const int ApplicationViewHeight = 189; + private List appIcons; + private readonly int maxAppsCount; + private int currentAppsCount; + + public static readonly BindableProperty AppListProperty = BindableProperty.Create(nameof(AppList), typeof(IEnumerable), typeof(ApplicationsView), null, propertyChanged: (bindable, oldValue, newValue) => + { + ApplicationsView instance = (ApplicationsView)bindable; + if (oldValue != newValue) + { + if (oldValue != null) + { + ((List)oldValue).Clear(); + } + if(newValue != null) + { + instance.appList = (IEnumerable)newValue; + } + instance.UpdateAppIcons(); + } + }, + defaultValueCreator: (bindable) => ((ApplicationsView)bindable).appList); + + public ApplicationsView(int appsCount) : base() + { + Name = "ApplicationsView"; + WidthSpecification = LayoutParamPolicies.MatchParent; + HeightSpecification = ApplicationViewHeight.SpToPx(); + CornerRadius = new Vector4(20, 20, 0, 0); + Layout = new LinearLayout() + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + CellPadding = new Size2D(16, 0).SpToPx() + }; + maxAppsCount = appsCount; + currentAppsCount = 0; + appList = new List(); + CreateDefaultAppItems(); + Tizen.Log.Info(Resources.LogTag, "ApplicationsView"); + } + + private void CreateDefaultAppItems() + { + appIcons = new List(); + for (int i = 0; i < maxAppsCount; i++) + { + AppItemView itemView = new AppItemView(); + itemView.Hide(); + itemView.LongPressed += (object sender, EventArgs e) => + { + AddDeleteOption(); + }; + appIcons.Add(itemView); + } + Tizen.Log.Info(Resources.LogTag, "Icons Added"); + } + + private IEnumerable appList; + public IEnumerable AppList + { + get => (IEnumerable)GetValue(AppListProperty); + set => SetValue(AppListProperty, value); + } + + private void UpdateAppIcons() + { + List appDataList = (List)appList; + int totalCount = appDataList.Count; + + if (totalCount <= currentAppsCount) + { + for (int i = 0; i < totalCount; i++) + { + appIcons[i].BindingContext = appDataList[i]; + } + for (int i = totalCount; i < currentAppsCount; i++) + { + AppItemView appItemView = appIcons[i]; + appItemView.Hide(); + Remove(appItemView); + } + } + else + { + for (int i = 0; i < currentAppsCount; i++) + { + appIcons[i].BindingContext = appDataList[i]; + } + for (int i = currentAppsCount; i < totalCount; i++) + { + AppItemView itemView = appIcons[i]; + itemView.Show(); + Add(itemView); + itemView.BindingContext = appDataList[i]; + itemView.Icon.SetBinding(ImageView.ResourceUrlProperty, "IconUrl"); + itemView.Label.SetBinding(TextLabel.TextProperty, "Name"); + itemView.SetBinding(AppItemView.AppSelectCommandProperty, "AppSelectCommand"); + } + } + currentAppsCount = totalCount; + Tizen.Log.Info(Resources.LogTag, "Icons Updated"); + } + + private void AddDeleteOption() + { + for(int i = 0; i < currentAppsCount - 1; i++) + { + appIcons[i].AddCrossButton(); + } + } + + protected override void Dispose(DisposeTypes type) + { + if (Disposed) + { + return; + } + if (type == DisposeTypes.Explicit) + { + while (appIcons.Count != 0) + { + AppItemView view = appIcons[0]; + appIcons.RemoveAt(0); + Remove(view); + view.Dispose(); + } + appIcons.Clear(); + appIcons = null; + } + Tizen.Log.Info(Resources.LogTag, "ApplicationsView Dispose"); + base.Dispose(type); + } + + public void DeleteView() + { + Dispose(DisposeTypes.Explicit); + } + } +} diff --git a/TrayApplication/Views/MainView.cs b/TrayApplication/Views/MainView.cs new file mode 100644 index 0000000..0576dcd --- /dev/null +++ b/TrayApplication/Views/MainView.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; +using Tizen.NUI.Xaml; +using TrayApplication.Common; +using TrayApplication.ViewModels; + +namespace TrayApplication.Views +{ + public class MainView : View + { + private ApplicationsView applicationsView; + private QuickAccessView quickAccessView; + private Animation animation; + + public MainView() : base() + { + Name = "MainView"; + WidthSpecification = LayoutParamPolicies.MatchParent; + HeightSpecification = LayoutParamPolicies.MatchParent; + CornerRadius = new Vector4(20, 20, 20, 20); + BackgroundColor = Color.Transparent; + Layout = new LinearLayout() + { + LinearOrientation = LinearLayout.Orientation.Vertical + }; + UpdateTheme(ThemeManager.PlatformThemeId); + int appsCount = Window.Instance.Size.Width / 154.SpToPx(); + ApplicationViewModel applicationViewModel = new ApplicationViewModel(appsCount); + applicationsView = new ApplicationsView(appsCount); + Add(applicationsView); + applicationsView.BindingContext = applicationViewModel; + applicationsView.SetBinding(BackgroundColorProperty, "BackgroundColor"); + applicationsView.SetBinding(ApplicationsView.AppListProperty, "ButtonsInfo"); + QuickAccessViewModel quickAccessViewModel = new QuickAccessViewModel(); + quickAccessView = new QuickAccessView(); + Add(quickAccessView); + quickAccessView.BindingContext = quickAccessViewModel; + quickAccessView.SetBinding(BackgroundColorProperty, "BackgroundColor"); + quickAccessView.SetBinding(QuickAccessView.AppListProperty, "ButtonsInfo"); + ThemeManager.ThemeChanged += OnThemeUpdated; + } + + private void OnThemeUpdated(object sender, ThemeChangedEventArgs e) + { + if (e.IsPlatformThemeChanged) + { + Tizen.Log.Error(Resources.LogTag, "Theme Changed: " + e.ThemeId); + UpdateTheme(e.PlatformThemeId); + } + } + + private void HideAnimation() + { + animation = new Animation(500); + animation.Play(); + animation.AnimateTo(this, "PositionY", 313.SpToPx()); + animation.Finished += OnExitAnimationFinished; + } + + private void OnExitAnimationFinished(object sender, EventArgs e) + { + Hide(); + } + + private void SetTheme(string path) + { + try + { + Theme theme = new Theme(path); + ThemeManager.ApplyTheme(theme); + } + catch (ArgumentNullException e) + { + Tizen.Log.Error(Resources.LogTag, "ArgumentNullException: " + e.ParamName); + } + catch (IOException e) + { + Tizen.Log.Error(Resources.LogTag, "IOException: " + e.Message); + } + catch (XamlParseException e) + { + Tizen.Log.Error(Resources.LogTag, "XamlParseException: " + e.Message); + if (e.XmlInfo != null) + { + Tizen.Log.Error(Resources.LogTag, "XamlParseException, LineNo." + e.XmlInfo.LineNumber + " Pos: " + e.XmlInfo.LinePosition + " HasInfo: " + e.XmlInfo.HasLineInfo().ToString()); + } + } + } + private void UpdateTheme(string platformThemeId) + { + if (platformThemeId == null) + { + Tizen.Log.Error(Resources.LogTag, "Platform theme id is null"); + return; + } + if (platformThemeId.Equals(Resources.LightPlatformThemeId)) + { + SetTheme(Resources.GetThemePath() + "light.xaml"); + } + else if (platformThemeId.Equals(Resources.DarkPlatformThemeId)) + { + SetTheme(Resources.GetThemePath() + "dark.xaml"); + } + } + + protected override void Dispose(DisposeTypes type) + { + if (Disposed) + { + return; + } + if (type == DisposeTypes.Explicit) + { + if(animation != null) + { + animation.Stop(); + animation.Dispose(); + animation = null; + } + if (applicationsView != null) + { + applicationsView.DeleteView(); + applicationsView = null; + Remove(applicationsView); + } + if (quickAccessView != null) + { + quickAccessView.DeleteView(); + quickAccessView = null; + Remove(quickAccessView); + } + Window.Instance.GetDefaultLayer().Remove(this); + } + Tizen.Log.Info(Resources.LogTag, "MainViewDispose"); + base.Dispose(type); + } + + public void ShowView() + { + Show(); + animation = new Animation(500); + animation.Play(); + animation.AnimateTo(this, "PositionY", 0.SpToPx()); + } + + public void HideView() + { + HideAnimation(); + } + } +} diff --git a/TrayApplication/Views/QuickAccessView.cs b/TrayApplication/Views/QuickAccessView.cs new file mode 100644 index 0000000..4eb3419 --- /dev/null +++ b/TrayApplication/Views/QuickAccessView.cs @@ -0,0 +1,121 @@ +using System.Collections; +using System.Collections.Generic; +using Tizen.NUI; +using Tizen.NUI.Components; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; +using TrayApplication.Common; + +namespace TrayApplication.Views +{ + public class QuickAccessView : View + { + private const int QuickAccessViewHeight = 124; + private const int IconSize = 76; + private List