--- /dev/null
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xm
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArfacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+.svace-dir/
+/feasibility/Homescreen/Homescreen/Homescreen.Tizen.Mobile/res/badge_bg.#.png
+
+# SVACE
+CSCC.err
+CompilationErrors*.txt
+csresults*.txt
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Runtime.CompilerServices;
+
+namespace Homescreen.Debug
+{
+ /// <summary>
+ /// DebugLog provides debugging APIs for the portable library project.
+ /// </summary>
+ public class DebugLog : ILog
+ {
+ private static readonly string TAG = "Homescreen";
+
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Debug(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Error(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(tag, message, file, func, line);
+ }
+ }
+
+ /// <summary>
+ /// TizenLog provides debugging APIs for the Tizen specific project.
+ /// </summary>
+ public class TizenLog
+ {
+ private static readonly string TAG = "Homescreen";
+
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Debug(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Error(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(tag, message, file, func, line);
+ }
+ }
+
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <!-- Property Group for Tizen40 Project -->
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>tizen40</TargetFramework>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>None</DebugType>
+ </PropertyGroup>
+
+ <!-- If solution already has PCL project, will reference -->
+
+
+ <!-- Include Nuget Package for Tizen Project building -->
+ <ItemGroup>
+ <PackageReference Include="Plugin.SQLite" Version="1.0.4" />
+ <PackageReference Include="sqlite-net-base" Version="1.5.166-beta" />
+ <PackageReference Include="sqlite-net-pcl" Version="1.4.118" />
+ <PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.8" />
+ <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.8" />
+ <PackageReference Include="Tizen.NET" Version="5.0.0-preview1-00412">
+ <ExcludeAssets>Runtime</ExcludeAssets>
+ </PackageReference>
+ <PackageReference Include="Tizen.NET.Sdk" Version="1.0.1-pre1" />
+ <PackageReference Include="Tizen.Xamarin.Forms.Extension" Version="2.4.0-v00014" />
+ <PackageReference Include="Xamarin.Forms.Platform.Tizen" Version="2.5.0.77107" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Homescreen\Homescreen\Homescreen.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="nunit.framework">
+ <HintPath>lib\nunit.framework.dll</HintPath>
+ </Reference>
+ <Reference Include="nunitlite">
+ <HintPath>lib\nunitlite.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+
+</Project>
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View;
+using Tizen.Xamarin.Forms.Extension;
+using Xamarin.Forms;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// AlertPopup makes a pop-up by given information
+ /// such as a title, a content and buttons.
+ /// Providing methods for setting by following IAlertPopup.
+ /// </summary>
+ public class AlertPopup : IAlertPopup
+ {
+ /// <summary>
+ /// A pop-up title
+ /// </summary>
+ public string Title
+ {
+ get => dialog.Title;
+ set => dialog.Title = value;
+ }
+
+ /// <summary>
+ /// A pop-up content view which will be displayed in a pop-up
+ /// </summary>
+ public Xamarin.Forms.View Content
+ {
+ get => dialog.Content;
+ set => dialog.Content = value;
+ }
+
+ /// <summary>
+ /// First button among three pop-up buttons.
+ /// </summary>
+ public Button FirstButton
+ {
+ get => dialog.Positive;
+ set => dialog.Positive = value;
+ }
+
+ /// <summary>
+ /// Second button among three pop-up buttons inside
+ /// </summary>
+ public Button SecondButton
+ {
+ get => dialog.Neutral;
+ set => dialog.Neutral = value;
+ }
+
+ /// <summary>
+ /// Third button among three pop-up buttons inside
+ /// </summary>
+ public Button ThirdButton
+ {
+ get => dialog.Negative;
+ set => dialog.Negative = value;
+ }
+
+ private Dialog dialog;
+
+ /// <summary>
+ /// A constructor which sets internal event handlers.
+ /// </summary>
+ public AlertPopup()
+ {
+ dialog = new Dialog
+ {
+ HorizontalOption = LayoutOptions.FillAndExpand
+ };
+
+ dialog.BackButtonPressed += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+
+ dialog.OutsideClicked += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+
+ dialog.Hidden += (s, arg) =>
+ {
+ Title = null;
+ Content = null;
+ FirstButton = null;
+ SecondButton = null;
+ ThirdButton = null;
+ };
+ }
+
+ /// <summary>
+ /// Hide a pop-up.
+ /// </summary>
+ public void Hide()
+ {
+ dialog.Hide();
+ }
+
+ /// <summary>
+ /// Show a pop-up.
+ /// </summary>
+ public void Show()
+ {
+ dialog.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.Model.Interface;
+using System;
+using Tizen.Applications;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// An app launching port
+ /// </summary>
+ /// <see cref="IAppLauncher"/>
+ public class AppLauncher : IAppLauncher
+ {
+ /// <summary>
+ /// A method launches an app which matched with the appId.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ public void LaunchApp(string appId)
+ {
+ AppControl handle = new AppControl
+ {
+ ApplicationId = appId
+ };
+ try
+ {
+ AppControl.SendLaunchRequest(handle);
+ }
+ catch (Exception e)
+ {
+ TizenLog.Error("AppControl is failed, " + e.Message);
+ return;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile.Ports;
+using System;
+using Homescreen.Model;
+using Tizen.Applications;
+using Xamarin.Forms;
+using Homescreen.DataModel;
+
+[assembly: Dependency(typeof(BadgeNotifier))]
+namespace Homescreen.Tizen.Mobile.Ports
+{
+ /// <summary>
+ /// BadgeNotifier manages event handler for badge notifications
+ /// co-working with Tizen application framework.
+ /// </summary>
+ public class BadgeNotifier : IBadgeEventNotifier
+ {
+ event EventHandler<BadgeUpdateEventArgs> BadgeEventHandler;
+
+ private void PackageManagerBadgeChanged(object sender, BadgeEventArgs e)
+ {
+ Badge badge = e.Badge;
+ if (e.Reason == BadgeEventArgs.Action.Update)
+ {
+ BadgeInformation badgeInfo = new BadgeInformation()
+ {
+ AppId = badge.AppId,
+ Count = badge.Count,
+ IsVisible = badge.Visible
+ };
+ BadgeEventHandler?.Invoke(this, new BadgeUpdateEventArgs() { UpdatedInformation = badgeInfo });
+ }
+ }
+
+ /// <summary>
+ /// Register an event handler to get updated badge notifications.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A registration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool Register(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ BadgeEventHandler += handler;
+ BadgeControl.Changed += PackageManagerBadgeChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A deregistration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool DeRegister(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ BadgeEventHandler -= handler;
+ BadgeControl.Changed -= PackageManagerBadgeChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Provides badge information.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ /// <returns>A badge information</returns>
+ /// <see cref="BadgeInformation"/>
+ BadgeInformation IBadgeEventNotifier.Get(string appId)
+ {
+ Badge badge;
+ try
+ {
+ badge = BadgeControl.Find(appId);
+
+ }
+ catch
+ {
+ return null;
+ }
+
+ BadgeInformation badgeInfo = new BadgeInformation()
+ {
+ AppId = badge.AppId,
+ Count = badge.Count,
+ IsVisible = badge.Visible
+ };
+
+ return badgeInfo;
+ }
+ }
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View;
+using System.Collections.Generic;
+using Tizen.Xamarin.Forms.Extension;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// An interface to make Pop-up menu.
+ /// </summary>
+ public class MenuPopup : IMenuPopup
+ {
+ private ContextPopup popup;
+ private IDictionary<string, ItemSelected> ItemSelectCallbackList = new Dictionary<string, ItemSelected>();
+ private bool isDismissed;
+
+ public bool IsDismissed => isDismissed;
+
+ /// <summary>
+ /// A method adds a pop-up menu item.
+ /// </summary>
+ /// <param name="title">A menu title</param>
+ /// <param name="itemSelected">A function will be called if the menu is selected.</param>
+ public void AddMenuItem(string title, ItemSelected itemSelected)
+ {
+ ItemSelectCallbackList.Add(title, itemSelected);
+ }
+
+ /// <summary>
+ /// Hide a pop-up menu.
+ /// </summary>
+ public void Hide()
+ {
+ popup.Dismiss();
+ }
+
+ /// <summary>
+ /// Show a pop-up menu.
+ /// </summary>
+ /// <param name="anchor">A view can be base of pop-up menu.</param>
+ public void Show(Xamarin.Forms.View anchor)
+ {
+ popup = new ContextPopup
+ {
+ DirectionPriorities = new ContextPopupDirectionPriorities(ContextPopupDirection.Down, ContextPopupDirection.Right, ContextPopupDirection.Left, ContextPopupDirection.Up),
+ IsAutoHidingEnabled = true,
+ };
+
+ foreach (var item in ItemSelectCallbackList)
+ {
+ popup.Items.Add(new ContextPopupItem(item.Key));
+ }
+
+ popup.SelectedIndexChanged += (s, e) =>
+ {
+ var title = popup.Items[popup.SelectedIndex]?.Label;
+ if (ItemSelectCallbackList.TryGetValue(title, out ItemSelected itemSelected))
+ {
+ itemSelected();
+ popup.Dismiss();
+ }
+ };
+
+ popup.Dismissed += (s, e) =>
+ {
+ isDismissed = true;
+ ItemSelectCallbackList.Clear();
+ };
+
+ isDismissed = false;
+ popup.Show(anchor);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile;
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Homescreen.Model;
+using System.Threading.Tasks;
+using Tizen.Applications;
+using Homescreen.Debug;
+using Homescreen.DataModel;
+
+[assembly: Dependency(typeof(PackageNotifier))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// PackageNotifier provides APIs related with packages in the device.
+ /// Also it manages given event handlers that will be called if a package is updated
+ /// with the Tizen applications framework.
+ /// </summary>
+ public class PackageNotifier : IPackageChanged
+ {
+
+ event EventHandler<PackageUpdateEventArgs> PackageManagerEventHandler;
+
+ private static String DefaultAppIcon = "default_app_icon.png";
+
+
+ /// <summary>
+ /// Register an event handler to get package modification events
+ /// </summary>
+ /// <param name="handler">An event handler will handle the package modification events</param>
+ /// <returns>A registration status</returns>
+ public bool Register(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ PackageManagerEventHandler += handler;
+ PackageManager.InstallProgressChanged += PackageManagerInstallProgressChanged;
+ PackageManager.UninstallProgressChanged += PackageManagerUninstallProgressChanged;
+ PackageManager.UpdateProgressChanged += PackageManagerUpdateProgressChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler
+ /// </summary>
+ /// <param name="handler">An event handler to be deregistered.</param>
+ /// <returns>A deregistration status</returns>
+ public bool DeRegister(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ PackageManagerEventHandler -= handler;
+ PackageManager.InstallProgressChanged -= PackageManagerInstallProgressChanged;
+ PackageManager.UninstallProgressChanged -= PackageManagerUninstallProgressChanged;
+ PackageManager.UpdateProgressChanged -= PackageManagerUpdateProgressChanged;
+
+ return true;
+ }
+
+ private void PackageManagerInstallProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Installed, });
+ }
+ }
+
+ private void PackageManagerUninstallProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Uninstalled, });
+ }
+ }
+
+ private void PackageManagerUpdateProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Updated, });
+ }
+ }
+
+ /// <summary>
+ /// Get all installed apps information.
+ /// </summary>
+ /// <returns>An installed app information list</returns>
+ public async Task<IList<InstalledAppInformation>> GetAllInstalledAppInformation()
+ {
+ try
+ {
+ List<InstalledAppInformation> resultList = new List<InstalledAppInformation>();
+ Dictionary<string, string> filters = new Dictionary<string, string>();
+
+ ApplicationInfoFilter filter = new ApplicationInfoFilter();
+ filter.Filter.Add(ApplicationInfoFilter.Keys.NoDisplay, "false");
+ // TODO : It is not supported yet
+ //filter.Filter.Add(ApplicationInfoFilter.Keys.InstalledStorage, ApplicationInfoFilter.Values.InstalledInternal);
+
+ Task<IEnumerable<ApplicationInfo>> task = ApplicationManager.GetInstalledApplicationsAsync(filter);
+
+ IEnumerable<ApplicationInfo> installedList = await task;
+
+ foreach (var appInfo in installedList)
+ {
+ if (appInfo.Label == null ||
+ appInfo.ApplicationId == null)
+ {
+ continue;
+ }
+
+ Package pkgInfo = PackageManager.GetPackage(appInfo.PackageId);
+
+ resultList.Add(new InstalledAppInformation
+ {
+ PackageId = appInfo.PackageId,
+ AppId = appInfo.ApplicationId,
+ Title = appInfo.Label,
+ IconPath = (System.IO.File.Exists(appInfo.IconPath)) ? appInfo.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ IsRemovable = pkgInfo.IsRemovable,
+ });
+ }
+
+ // TODO : It is not supported yet
+ resultList.Sort((InstalledAppInformation a, InstalledAppInformation b) =>
+ {
+ return a.Title.CompareTo(b.Title);
+ });
+#if (false)
+ //filter.Filter.Remove(ApplicationInfoFilter.Keys.InstalledStorage);
+ //filter.Filter.Add(ApplicationInfoFilter.Keys.InstalledStorage, ApplicationInfoFilter.Values.InstalledExternal);
+
+ task = ApplicationManager.GetInstalledApplicationsAsync(filter);
+
+ IEnumerable<ApplicationInfo> installedExternalList = await task;
+
+ foreach (var appInfoForExternal in installedExternalList)
+ {
+
+ if (appInfoForExternal.Label == null ||
+ appInfoForExternal.ApplicationId == null)
+ {
+ continue;
+ }
+
+ resultList.Add(new InstalledAppInformation
+ {
+ PackageId = appInfoForExternal.PackageId,
+ AppId = appInfoForExternal.ApplicationId,
+ Title = appInfoForExternal.Label,
+ IconPath = (System.IO.File.Exists(appInfoForExternal.IconPath)) ? appInfoForExternal.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ });
+ }
+#endif
+ return resultList;
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug(e.Message);
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Get an installed app information matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>A matching app information.</returns>
+ public InstalledAppInformation GetInstalledAppInformation(string applicationId)
+ {
+ ApplicationInfo appInfo = ApplicationManager.GetInstalledApplication(applicationId);
+ Package pkgInfo = PackageManager.GetPackage(appInfo.PackageId);
+
+ InstalledAppInformation newInfo = new InstalledAppInformation()
+ {
+ PackageId = appInfo.PackageId,
+ AppId = appInfo.ApplicationId,
+ Title = appInfo.Label,
+ IconPath = (System.IO.File.Exists(appInfo.IconPath)) ? appInfo.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ IsRemovable = pkgInfo.IsRemovable,
+ };
+
+ return newInfo;
+ }
+
+ /// <summary>
+ /// Uninstall an app matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>An uninstall status</returns>
+ public bool RequestUninstall(string applicationId)
+ {
+ return PackageManager.Uninstall(applicationId);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Tizen.Applications;
+using System.Collections.Generic;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.UITest.Tizen.Mobile;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// Manage the instance of RemoteView to prevent it
+ /// from being regenerated every time the Parent of Remoteview changes.
+ /// </summary>
+ public class RemoteViewStorage : IRemoteViewStorage
+ {
+ private static IDictionary<long, ReusableRemoteView> WidgetStorage = new Dictionary<long, ReusableRemoteView>();
+
+ /// <summary>
+ /// Returns the instance of the widget.
+ /// The instance may already be stored, or it may be a newly created instance.
+ /// </summary>
+ /// <param name="id">A unique ID that identifies the widget.</param>
+ /// <param name="widgetId">A widget ID that identifies the widget in the Tizen platform.</param>
+ /// <param name="content">A widget content</param>
+ /// <param name="period">A widget update period.</param>
+ /// <param name="previewImage">A widget preview image.</param>
+ /// <param name="overlayText">A text message displaying on the widget.</param>
+ /// <param name="loadingMessage">A text message during the widget is initializing.</param>
+ /// <returns>A instance of the widget</returns>
+ public static ReusableRemoteView Get(long id, string widgetId, string content,
+ double period, bool previewImage = true,
+ bool overlayText = true, bool loadingMessage = true)
+ {
+ if (WidgetStorage.TryGetValue(id, out ReusableRemoteView widget))
+ {
+ return widget;
+ }
+
+ var remoteView = new ReusableRemoteView(widgetId, content, period, previewImage, overlayText, loadingMessage);
+ WidgetStorage.Add(id, remoteView);
+ return remoteView;
+ }
+
+ /// <summary>
+ /// Initializes RemoteViewFactory.
+ /// </summary>
+ /// <param name="win">A base window for the widgets.</param>
+ public static void Init(EvasObject win)
+ {
+ RemoteViewFactory.Init(win);
+ }
+
+ /// <summary>
+ /// Finalizes the RemoteViewFactory.
+ /// </summary>
+ public static void Shutdown()
+ {
+ RemoteViewFactory.Shutdown();
+ }
+
+ private static void DeleteRemoteView(long id)
+ {
+ try
+ {
+ if (WidgetStorage.TryGetValue(id, out ReusableRemoteView widget))
+ {
+ widget.Layout.Hide();
+ WidgetStorage.Remove(id);
+ }
+ }
+ catch (Exception e)
+ {
+ TizenLog.Error($"{e.Message}");
+ }
+ }
+
+ /// <summary>
+ /// Delete the instance of the stored widget.
+ /// </summary>
+ /// <param name="widgetID">A widget ID.</param>
+ public void Delete(long widgetID)
+ {
+ DeleteRemoteView(widgetID);
+ }
+
+ public static void Print(string tag)
+ {
+ foreach (var item in WidgetStorage)
+ {
+ TizenLog.Debug(tag, message: $"{item.Key}, {item.Value.ID}");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Tizen.Applications;
+using Homescreen.View;
+using System;
+using Homescreen.UITest.Tizen.Mobile;
+
+namespace Homescreen.Tizen.Mobile
+{
+ public class ReusableRemoteView
+ {
+ public Container Layout => box;
+ public IMouseEventReceiver Receiver
+ {
+ get => receiver;
+ set
+ {
+ receiver = value;
+ mouseEvent.Receiver = receiver;
+ }
+ }
+
+ public bool RepeatEvents
+ {
+ get => touchLayer.RepeatEvents;
+ set => touchLayer.RepeatEvents = value;
+ }
+
+ public string ID => remoteView.Id;
+
+ private RemoteView remoteView;
+ private IMouseEventReceiver receiver;
+ private MouseEvent mouseEvent;
+
+ private Box box;
+ private Rectangle touchLayer;
+
+ public ReusableRemoteView(string widgetId, string content, double period, bool previewImage = true, bool overlayText = true, bool loadingMessage = true)
+ {
+ box = new Box(Program.Window);
+ remoteView = RemoteViewFactory.Create(box, widgetId, content, period, previewImage, overlayText, loadingMessage);
+
+ touchLayer = new Rectangle(box)
+ {
+ Color = Color.Transparent,
+ RepeatEvents = true
+ };
+ touchLayer.Show();
+
+ box.PackEnd(remoteView.Layout);
+ box.PackEnd(touchLayer);
+
+ box.SetLayoutCallback(() =>
+ {
+ remoteView.Layout.Geometry = box.Geometry;
+ touchLayer.Geometry = box.Geometry;
+ });
+
+ mouseEvent = new MouseEvent(touchLayer, receiver);
+ }
+
+ public void Dispose()
+ {
+ mouseEvent.Detach();
+ }
+
+ public void SendEvent(RemoteView.Event ev)
+ {
+ remoteView.SendEvent(ev);
+ }
+
+ public void Resize(int width, int height)
+ {
+ remoteView.Layout.Resize(width, height);
+ box.Resize(width, height);
+ }
+
+ public void Show()
+ {
+ remoteView.Layout.Show();
+ box.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+using SQLite;
+using Xamarin.Forms;
+using SQLitePCL;
+using Homescreen.Tizen.Mobile;
+using Homescreen.Model.Interface;
+
+[assembly: Dependency(typeof(SQLitePort))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// SQLitePort manages SQLite to provide storages which is using for saving Widgets and Apps
+ /// </summary>
+ public class SQLitePort : ISQLite
+ {
+ /// <summary>
+ /// Create table
+ /// </summary>
+ /// <typeparam name="T">A DB item type that will be used to make a table.</typeparam>
+ /// <param name="conn">A SQLite connection</param>
+ public void CreateTable<T>(SQLiteConnection conn)
+ {
+ conn.CreateTable<T>();
+ }
+
+ /// <summary>
+ /// Provides SQLite connection which is main interface for data basing.
+ /// </summary>
+ /// <returns>A connection of SQLite</returns>
+ public SQLiteConnection GetConnection()
+ {
+ return GetConnection("homescreen.db3");
+ }
+
+ public SQLiteConnection GetConnection(string dbFilename)
+ {
+ raw.SetProvider(new SQLite3Provider_sqlite3());
+ raw.FreezeProvider(true);
+
+ string documentsPath = global::Tizen.Applications.Application.Current.DirectoryInfo.Data;
+ var path = Path.Combine(documentsPath, dbFilename);
+ SQLiteConnection conn = new SQLiteConnection(path);
+
+ return conn;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.UITest.Tizen.Mobile;
+using Homescreen.ViewModel;
+using System.Collections.Generic;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// TizenDeviceInfo provides APIs regarding screen size getting, status bar managing.
+ /// </summary>
+ public class TizenDeviceInfo : IDeviceInfo
+ {
+ /// <summary>
+ /// A width of device screen size
+ /// </summary>
+ public int Width => Program.ScreenSize.Width;
+
+ /// <summary>
+ /// A height of device screen size
+ /// </summary>
+ public int Height => Program.ScreenSize.Height;
+
+ private IDictionary<ElmSharp.StatusBarMode, StatusBarMode> BarMode = new Dictionary<ElmSharp.StatusBarMode, StatusBarMode>()
+ {
+ { ElmSharp.StatusBarMode.Opaque, StatusBarMode.Opaque },
+ { ElmSharp.StatusBarMode.Translucent, StatusBarMode.Translucent },
+ { ElmSharp.StatusBarMode.Transparent, StatusBarMode.Transparent },
+ };
+
+ /// <summary>
+ /// Mode of status bar displaying
+ /// </summary>
+ /// <see cref="StatusBarMode"/>
+ public StatusBarMode StatusBarMode
+ {
+ get => BarMode[Program.Window.StatusBarMode];
+ set
+ {
+ foreach (var mode in BarMode)
+ {
+ if (mode.Value == value)
+ {
+ Program.Window.StatusBarMode = mode.Key;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Interface;
+using Tizen.Xamarin.Forms.Extension;
+
+namespace Homescreen.Tizen.Mobile.Ports
+{
+ /// <summary>
+ /// Display a toast pop-up by using Tizen Toast extension
+ /// </summary>
+ public class ToastPopup : IToastPopup
+ {
+ /// <summary>
+ /// Display a toast pop-up with a given text.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ public void Display(string text)
+ {
+ Toast.DisplayText(text);
+ }
+
+ /// <summary>
+ /// Display a toast pop-up with a give text during given duration.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ /// <param name="duration">A display time in second.</param>
+ public void Display(string text, int duration)
+ {
+ Toast.DisplayText(text, duration);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Tizen.System;
+using Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile;
+using Tizen.Applications;
+
+[assembly: Dependency(typeof(WallpaperInfo))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WallpaperInfo provides APIs regarding device's wallpaper
+ /// co-working the Tizen application framework.
+ /// </summary>
+ public class WallpaperInfo : IWallpaperChanged
+ {
+ static public bool IsWrongPath;
+
+ event EventHandler WallpaperEventHandler;
+
+ private void WallpaperHomeScreenChanged(object sender, WallpaperHomeScreenChangedEventArgs e)
+ {
+ WallpaperEventHandler?.Invoke(this, null);
+ }
+
+ /// <summary>
+ /// Get wallpaper full path.
+ /// </summary>
+ /// <returns>a wallpaper path</returns>
+ public string GetWallpaperPath()
+ {
+ return IsWrongPath ? "wrong_path" : SystemSettings.WallpaperHomeScreen;
+ }
+
+ /// <summary>
+ /// Register an event handler to receive wallpaper update events.
+ /// </summary>
+ /// <param name="handler">An event handler will receive wallpaper update events</param>
+ public void RegisterWallpaperChangedCallback(EventHandler handler)
+ {
+ WallpaperEventHandler += handler;
+
+ if (WallpaperEventHandler.GetInvocationList().Length == 1)
+ {
+ SystemSettings.WallpaperHomeScreenChanged += WallpaperHomeScreenChanged;
+ }
+ }
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler will be deregistered.</param>
+ public void DeregisterWallpaperChangedCallback(EventHandler handler)
+ {
+ WallpaperEventHandler -= handler;
+ if (WallpaperEventHandler == null || WallpaperEventHandler.GetInvocationList().Length == 0)
+ {
+ SystemSettings.WallpaperHomeScreenChanged -= WallpaperHomeScreenChanged;
+ }
+ }
+
+ /// <summary>
+ /// Launch wallpaper settings to replace with another one.
+ /// </summary>
+ public void LaunchWallpaperSetting()
+ {
+ AppControl handle = new AppControl
+ {
+ ApplicationId = "org.tizen.wallpaper-ui-service",
+ Operation = AppControlOperations.Main,
+ };
+ handle.ExtraData.Add("from", "Homescreen-efl");
+ handle.ExtraData.Add("popup_type", "selection_popup");
+ handle.ExtraData.Add("setas-type", "Homescreen");
+ AppControl.SendLaunchRequest(handle);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.Collections.Generic;
+using System;
+using Tizen.Applications;
+using Homescreen.Model;
+using Homescreen.Debug;
+using Homescreen.DataModel;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WidgetManager provides APIs regarding application's widgets
+ /// by co-working with the Tizen applications framework.
+ /// </summary>
+ public class WidgetManager : IWidgetManager
+ {
+ /// <summary>
+ /// A widget list property.
+ /// </summary>
+ public List<WidgetInformation> WidgetList => GetWidgetList();
+
+ private IDictionary<WidgetControl.Scale.SizeType, WidgetInformation.SizeType> SizeTypes = new Dictionary<WidgetControl.Scale.SizeType, WidgetInformation.SizeType>()
+ {
+ { WidgetControl.Scale.SizeType.Basic4x2, WidgetInformation.SizeType.SIZE_4x2 },
+ { WidgetControl.Scale.SizeType.Basic4x4, WidgetInformation.SizeType.SIZE_4x4 },
+ };
+
+ /// <summary>
+ /// Get widgets information matching widgetId
+ /// </summary>
+ /// <param name="widgetId">A widget ID</param>
+ /// <returns>A list of widgets information</returns>
+ public List<WidgetInformation> GetWidgetInfo(string widgetId)
+ {
+ List<WidgetInformation> list = new List<WidgetInformation>();
+
+ WidgetControl widgetControl = new WidgetControl(widgetId);
+ IEnumerable<WidgetControl.Scale> scales;
+ string widgetName;
+
+ try
+ {
+ scales = widgetControl.GetScales();
+ widgetName = widgetControl.GetName("");
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug(e.Message);
+ return list;
+ }
+
+ foreach (var item in scales)
+ {
+ if (SizeTypes.TryGetValue(item.Type, out WidgetInformation.SizeType type))
+ {
+ list.Add(new WidgetInformation()
+ {
+ PreviewImagePath = item.PreviewImagePath,
+ Name = widgetName,
+ Type = SizeTypes[item.Type],
+ });
+ }
+ }
+
+ return list;
+ }
+
+ public List<WidgetInformation> GetWidgetList()
+ {
+ List<WidgetInformation> list = new List<WidgetInformation>();
+
+ foreach (var package in PackageManager.GetPackages())
+ {
+ string[] widgetLsit = WidgetControl.GetWidgetIds(package.Id);
+ foreach (var widgetId in widgetLsit)
+ {
+ foreach (var widget in GetWidgetInfo(widgetId))
+ {
+ widget.PackageId = package.Id;
+ widget.WidgetId = widgetId;
+ list.Add(widget);
+ }
+ }
+ }
+
+ return list;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 NUnitLite.TUnit;
+using Xamarin.Forms;
+
+using Homescreen.Debug;
+using Tizen.Applications;
+using Tizen.Xamarin.Forms.Extension.Renderer;
+using ElmSharp;
+using Homescreen.Tizen.Mobile;
+using System;
+using Homescreen.Tizen.Mobile.Ports;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ /// <summary>
+ /// Tizen mobile home screen reference application.
+ /// This app provides functions regarding widgets display, widget management,
+ /// app list display, app launch, app remove and wallpaper selection.
+ /// Users can set their own widget pages with various widgets on the home screen in Widgets page.
+ /// Also, browse installed apps, managing apps and run an app from the Apps page.
+ /// </summary>
+ class Program : global::Xamarin.Forms.Platform.Tizen.FormsApplication
+ {
+ /// <summary>
+ /// Device screen size
+ /// </summary>
+ static public ElmSharp.Size ScreenSize { get; private set; }
+
+ public static App App { get; private set; }
+ /// <summary>
+ /// App's window instance.
+ /// </summary>
+ static public Window Window { get; private set; }
+
+ protected override void OnCreate()
+ {
+ base.OnCreate();
+ App = new App();
+ LoadApplication(App);
+ MainWindow.AvailableRotations = DisplayRotation.Degree_0;
+ MainWindow.StatusBarMode = StatusBarMode.Translucent;
+
+ RemoteViewStorage.Init(MainWindow);
+
+ Window = MainWindow;
+ ScreenSize = MainWindow.ScreenSize;
+
+ Device.StartTimer(TimeSpan.FromSeconds(3), () =>
+ {
+ TRunner t = new TRunner("Homescreen.UITest.Tizen.Mobile");
+ t.LoadTestsuite();
+ t.Execute();
+
+ return false;
+ });
+ }
+
+ protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
+ {
+ base.OnAppControlReceived(e);
+
+ if (e.ReceivedAppControl.Operation == AppControlOperations.Default)
+ {
+ if (e.ReceivedAppControl.ExtraData.TryGet("__HOME_OP__", out string value))
+ {
+ if (value == "__LAUNCH_BY_HOME_KEY__")
+ {
+ App?.LaunchByHome();
+ }
+ }
+ }
+ }
+
+ protected override void OnTerminate()
+ {
+ base.OnTerminate();
+ RemoteViewStorage.Shutdown();
+ }
+
+ static void Main(string[] args)
+ {
+ DependencyService.Register<DebugLog>();
+ DependencyService.Register<TizenDeviceInfo>();
+ DependencyService.Register<AppLauncher>();
+ DependencyService.Register<WidgetManager>();
+ DependencyService.Register<AlertPopup>();
+ DependencyService.Register<MenuPopup>();
+ DependencyService.Register<ToastPopup>();
+ DependencyService.Register<RemoteViewStorage>();
+
+ var app = new Program();
+ TizenFormsExtension.Init();
+ global::Xamarin.Forms.Platform.Tizen.Forms.Init(app);
+ app.Run(args);
+
+ app.Dispose();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View;
+using System;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(ClickableImage), typeof(ClickableImageRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// ClickableImageRenderer is a custom renderer for the CliableImage is like image button.
+ /// This custom renderer handles image replacing by the occurring events.
+ /// </summary>
+ public class ClickableImageRenderer : ImageRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+
+
+ if (Control == null)
+ {
+ return;
+ }
+
+ ClickableImage image = args.NewElement as ClickableImage;
+ if (image == null)
+ {
+ return;
+ }
+
+ /* If you change the value of Control.Color directly here, the changed value will not be applied.
+ * You should use the timer until the problem is resolved. */
+ Xamarin.Forms.Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.NormalColor.R * 255), (int)(image.NormalColor.G * 255), (int)(image.NormalColor.B * 255));
+ }
+
+ return false;
+ });
+
+ image.MouseDown += (s, e) =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.PressedColor.R * 255), (int)(image.PressedColor.G * 255), (int)(image.PressedColor.B * 255));
+ }
+ };
+ image.MouseUp += (s, e) =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.NormalColor.R * 255), (int)(image.NormalColor.G * 255), (int)(image.NormalColor.B * 255));
+ }
+ };
+ mouseEvent = new MouseEvent(Control, image);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(EntryView), typeof(EntryViewRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// EntryViewRenderer is a custom renderer of EntryView for the text typing.
+ /// </summary>
+ public class EntryViewRenderer : EntryRenderer
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Entry> args)
+ {
+ base.OnElementChanged(args);
+
+ if (Control != null)
+ {
+ Control.SetInputPanelReturnKeyType(InputPanelReturnKeyType.Done);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View.Widgets;
+using System.Collections.Generic;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(FastScrollLayout), typeof(FastScrollLayoutRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// FastScrollLayoutRenderer is custom renderer of FastScrollLayout which displays index with a scrolling function for the list like GUI controls.
+ /// </summary>
+ public class FastScrollLayoutRenderer : LayoutRenderer
+ {
+ private Index index;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> arg)
+ {
+ base.OnElementChanged(arg);
+
+ if (Control == null || arg.NewElement == null)
+ {
+ return;
+ }
+
+ FastScrollLayout scrollLayout = arg.NewElement as FastScrollLayout;
+ if (scrollLayout == null)
+ {
+ return;
+ }
+
+ if (index == null)
+ {
+ index = new Index(Control)
+ {
+ IsHorizontal = false,
+ AutoHide = false,
+ OmitEnabled = true,
+ };
+ index.Show();
+
+ var indexList = new List<string>
+ {
+ "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X", "Y", "Z"
+ };
+
+ foreach (var indexString in indexList)
+ {
+ var indexItem = index.Append(indexString);
+ indexItem.Selected += ((s, evt) =>
+ {
+ if (Element is FastScrollLayout layout)
+ {
+ layout.IndexSelected.Invoke(this, new IndexSelectedArgs()
+ {
+ Key = indexString,
+ });
+ }
+ });
+ }
+
+ index.Update(0);
+ Control.PackEnd(index);
+ Control.SetLayoutCallback(() =>
+ {
+ index.RaiseTop();
+ index.Geometry = Control.Geometry;
+ });
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+using Homescreen.View;
+using Homescreen.Debug;
+using Homescreen.Tizen.Mobile;
+
+[assembly: ExportRenderer(typeof(HomeScrollView), typeof(HomeScrollViewRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// HomeScrollViewRenderer is a custom renderer which handles HomeScrollView.
+ /// </summary>
+ ///<see cref="HomeScrollView"/>
+ public class HomeScrollViewRenderer : ScrollViewRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+
+ if (Control is Scroller scroller)
+ {
+ scroller.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ scroller.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ scroller.HorizontalRelativePageSize = 1;
+ scroller.HorizontalPageScrollLimit = 1;
+
+ mouseEvent = new MouseEvent(Control, args.NewElement as HomeScrollView);
+ }
+
+ if (args.NewElement != null && args.NewElement is HomeScrollView newScrollView)
+ {
+ newScrollView.HomeScrollViewScrollLockEventHandler += HomeScrollViewScrollLock;
+ newScrollView.HomeScrollViewScrollUnLockEventHandler += HomeScrollViewScrollUnlock;
+ }
+
+ if (args.OldElement != null && args.OldElement is HomeScrollView oldScrollview)
+ {
+ oldScrollview.HomeScrollViewScrollLockEventHandler -= HomeScrollViewScrollLock;
+ oldScrollview.HomeScrollViewScrollUnLockEventHandler -= HomeScrollViewScrollUnlock;
+ }
+ }
+
+ private void HomeScrollViewScrollLock(object sender, EventArgs e)
+ {
+ TizenLog.Debug($"HomeScrollViewScrollLock called");
+ (Control as Scroller).ScrollBlock = ScrollBlock.Horizontal;
+ }
+
+ private void HomeScrollViewScrollUnlock(object sender, EventArgs e)
+ {
+ TizenLog.Debug($"HomeScrollViewScrollUnlock called");
+ (Control as Scroller).ScrollBlock = ScrollBlock.None;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Debug;
+using Homescreen.View;
+using Xamarin.Forms;
+using Homescreen.View.Widgets;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// LongPressedTimer is a timer which is designed for the long press timing check.
+ /// </summary>
+ public class LongPressedTimer
+ {
+ /// <summary>
+ /// A flag indicates whether LongPressTimer is running or not.
+ /// </summary>
+ public bool IsRunning { get; private set; }
+
+ /// <summary>
+ /// A delegate of long press timer.
+ /// </summary>
+ public delegate void CompleteCallback();
+
+ private CompleteCallback longPressedCallback;
+ private int runningTime;
+
+ private bool isUsed;
+
+ /// <summary>
+ /// A setter method for the long pressed timer which will be called if long press timer is expired.
+ /// </summary>
+ /// <param name="callback">A delegate for mouse events</param>
+ public LongPressedTimer(CompleteCallback callback)
+ {
+ longPressedCallback = callback;
+ }
+
+ /// <summary>
+ /// Stop this timer.
+ /// </summary>
+ public void Stop()
+ {
+ IsRunning = false;
+ }
+
+ /// <summary>
+ /// Start a long press timer.
+ /// </summary>
+ public void Start()
+ {
+ if (isUsed)
+ {
+ return;
+ }
+
+ IsRunning = true;
+ isUsed = true;
+
+ runningTime = 0;
+ Device.StartTimer(TimeSpan.FromMilliseconds(5), () =>
+ {
+ runningTime += 5;
+ if (IsRunning == false)
+ {
+ return false;
+ }
+
+ if (runningTime >= 700)
+ {
+ IsRunning = false;
+ longPressedCallback();
+ return false;
+ }
+
+ return true;
+ });
+ }
+ }
+
+ /// <summary>
+ /// MouseEvent is a helper class that makes a Xamarin.Forms GUI element can handle mouse down, up, move events of a Tizen specific GUI control.
+ /// </summary>
+ public class MouseEvent
+ {
+ public IMouseEventReceiver Receiver { get; set; }
+
+ private EvasObjectEvent mouseDown;
+ private EvasObjectEvent mouseUp;
+ private EvasObjectEvent mouseMove;
+
+ private bool IsPressed;
+ private Xamarin.Forms.Point Down;
+ private Xamarin.Forms.Point Current;
+
+ private LongPressedTimer longTimer;
+
+ private static VisualElement topObject = null;
+
+ private bool IsChild(VisualElement parent, VisualElement child)
+ {
+ if (child == null)
+ {
+ return false;
+ }
+
+ var p = child.Parent;
+ while (p != null)
+ {
+ if (p == parent)
+ {
+ return true;
+ }
+
+ p = p.Parent as VisualElement;
+ }
+
+ return false;
+ }
+
+ public MouseEvent(EvasObject sender, IMouseEventReceiver receiver)
+ {
+ Receiver = receiver;
+
+ mouseDown = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseDown);
+ mouseDown.On += (s, e) =>
+ {
+
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (topObject == null)
+ {
+ topObject = Receiver as VisualElement;
+ }
+ else
+ {
+ if (IsChild(topObject, Receiver as VisualElement))
+ {
+ topObject = Receiver as VisualElement;
+ }
+ }
+
+ Device.StartTimer(TimeSpan.FromMilliseconds(10), () =>
+ {
+ if (topObject != (Receiver as VisualElement))
+ {
+ return false;
+ }
+
+ IsPressed = true;
+
+ Down = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+
+ Receiver.MouseDown?.Invoke(Receiver, new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ });
+
+ longTimer?.Stop();
+ longTimer = new LongPressedTimer(() =>
+ {
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseHold?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+ });
+ longTimer.Start();
+
+ return false;
+ });
+ };
+
+ mouseMove = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseMove);
+ mouseMove.On += (s, e) =>
+ {
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (IsPressed == false)
+ {
+ return;
+ }
+
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseMove?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+
+ if (longTimer.IsRunning && (Math.Pow(Current.X - Down.X, 2) + Math.Pow(Current.Y - Down.Y, 2) > 100))
+ {
+ longTimer.Stop();
+ }
+ };
+
+ mouseUp = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseUp);
+ mouseUp.On += (s, e) =>
+ {
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (IsPressed == false)
+ {
+ return;
+ }
+
+ topObject = null;
+
+ IsPressed = false;
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseUp?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+
+ if (longTimer.IsRunning)
+ {
+ longTimer.Stop();
+ Receiver.MouseClick?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+ }
+ };
+ }
+
+ /// <summary>
+ /// remove all registered callback for the Tizen specific GUI control.
+ /// </summary>
+ public void Detach()
+ {
+ longTimer?.Stop();
+
+ mouseDown?.Dispose();
+ mouseUp?.Dispose();
+ mouseMove?.Dispose();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Tizen.Mobile.Renderer;
+using Xamarin.Forms.Platform.Tizen;
+using Homescreen.View.Widgets;
+using Homescreen.View;
+
+[assembly: ExportRenderer(typeof(AllpageThumbnailLayout), typeof(MouseEventRenderer))]
+namespace Homescreen.Tizen.Mobile.Renderer
+{
+ /// <summary>
+ /// MouseEventRenderer is a custom renderer for mouse events.
+ /// The MouseEventRenderer provides controls to the MouseEvent for mouse event handling after element creating.
+ /// </summary>
+ /// <see cref="MouseEvent"/>
+ public class MouseEventRenderer : LayoutRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+ if (args.NewElement is IMouseEventReceiver receiver)
+ {
+ mouseEvent = new MouseEvent(Control, receiver);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Tizen.Mobile.Renderer;
+using Homescreen.View;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(NinePatch), typeof(NinePatchRenderer))]
+namespace Homescreen.Tizen.Mobile.Renderer
+{
+ /// <summary>
+ /// A custom renderer for NinePatchImage
+ /// </summary>
+ /// <see cref="NinePatch"/>
+ class NinePatchRenderer : ImageRenderer
+ {
+ /// <summary>
+ /// Updates border when Element is changed
+ /// </summary>
+ /// <param name="args">An image element changed event's argument </param>
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+ {
+ base.OnElementChanged(args);
+ UpdateBorder();
+ }
+
+ /// <summary>
+ /// Updates border when ElementProperty is changed
+ /// </summary>
+ /// <param name="sender">The source of the event</param>
+ /// <param name="args">An image element property changed event's argument </param>
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ if ((args.PropertyName == NinePatch.BorderBottomProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderLeftProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderRightProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderTopProperty.PropertyName))
+ {
+ UpdateBorder();
+ }
+
+
+ base.OnElementPropertyChanged(sender, args);
+ }
+
+ /// <summary>
+ /// A method for updating image color of Control
+ /// </summary>
+
+
+ /// <summary>
+ /// A method updates border of Control(Native Image)
+ /// </summary>
+ void UpdateBorder()
+ {
+ var img = Element as NinePatch;
+ Control?.SetBorder(img.BorderLeft, img.BorderRight, img.BorderTop, img.BorderBottom);
+ }
+
+ /// <summary>
+ /// A method updates border of Control(Native Image) after loading
+ /// </summary>
+ protected override void UpdateAfterLoading()
+ {
+ base.UpdateAfterLoading();
+ UpdateBorder();
+
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms.Platform.Tizen;
+using Tizen.Applications;
+using Homescreen.View.Widgets;
+using Homescreen.Tizen.Mobile;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.DataModel;
+
+[assembly: ExportRenderer(typeof(WidgetLayout), typeof(WidgetLayoutRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WidgetLayoutRenderer is a custom renderer for the WidgetLayout.
+ /// The WidgetLayout displays an app's widget in the view.
+ /// </summary>
+ public class WidgetLayoutRenderer : LayoutRenderer
+ {
+ private ReusableRemoteView remoteView;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> args)
+ {
+ base.OnElementChanged(args);
+
+ if (Control == null || args.NewElement == null)
+ {
+ return;
+ }
+
+ if (args.OldElement is WidgetLayout oldLayout)
+ {
+ oldLayout.WidgetSendCancelEventHandler = null;
+ oldLayout.WidgetTouchBlockEventHandler = null;
+ oldLayout.WidgetTouchUnlockEventHandler = null;
+ }
+
+ WidgetLayout widgetLayout = args.NewElement as WidgetLayout;
+ if (widgetLayout == null)
+ {
+ return;
+ }
+
+ try
+ {
+ remoteView = RemoteViewStorage.Get(
+ widgetLayout.WidgetInfo.Id, widgetLayout.WidgetInfo.WidgetId, widgetLayout.WidgetInfo.WidgetId, 0.0, true, false, false);
+
+ using (WidgetControl widgetControl = new WidgetControl(widgetLayout.WidgetInfo.WidgetId))
+ {
+ IEnumerable<WidgetControl.Scale> widgetScales = widgetControl.GetScales();
+ foreach (var scale in widgetScales)
+ {
+ if (widgetLayout.WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x4 && scale.Type == WidgetControl.Scale.SizeType.Basic4x4)
+ {
+ widgetLayout.WidthRequest = scale.Width;
+ widgetLayout.HeightRequest = scale.Height;
+ remoteView.Resize(scale.Width, scale.Height);
+
+ break;
+ }
+
+ if (widgetLayout.WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 && scale.Type == WidgetControl.Scale.SizeType.Basic4x2)
+ {
+ widgetLayout.WidthRequest = scale.Width;
+ widgetLayout.HeightRequest = scale.Height;
+ remoteView.Resize(scale.Width, scale.Height);
+ break;
+ }
+ }
+ }
+
+ remoteView.Show();
+ Control.PackEnd(remoteView.Layout);
+
+ Control.SetLayoutCallback(() =>
+ {
+ remoteView.Layout.Geometry = Control.Geometry;
+ });
+
+ widgetLayout.WidgetSendCancelEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Cancel ");
+ try
+ {
+ remoteView?.SendEvent(RemoteView.Event.CancelClick);
+ }
+ catch (Exception exception)
+ {
+ TizenLog.Error($"Failed to sendEvent(RemoteView.Event.CancelClick): {exception.Message}");
+ }
+ };
+
+ remoteView.Receiver = widgetLayout;
+
+ widgetLayout.WidgetTouchBlockEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Block ");
+ remoteView.RepeatEvents = false;
+ };
+
+ widgetLayout.WidgetTouchUnlockEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Unlock ");
+ remoteView.RepeatEvents = true;
+ };
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug("Failed to create RemoteView : " + e.Message);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Control?.UnPack(remoteView.Layout);
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+<StyleCopSettings Version="105">
+ <GlobalSettings>
+ <StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
+ </GlobalSettings>
+ <Analyzers>
+ <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EnumerationItemsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustContainValidXml">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VoidReturnValueMustNotBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustMatchAccessors">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustNotBeEmpty">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustContainWhitespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustMeetCharacterPercentage">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationHeadersMustNotContainBlankLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludedDocumentationXPathDoesNotExist">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InheritDocMustBeUsedWithInheritingClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustBeSpelledCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMustHaveHeader">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustShowCopyright">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveCopyrightText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustContainFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveValidCompanyText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
+ <Rules>
+ <Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotContainUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotUseHungarianNotation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VariableNamesMustNotBePrefixed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotBeginWithUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeadersMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleBlankLinesInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ChainedStatementBlocksMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="WhileDoFooterMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeSeparatedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtStartOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtEndOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules">
+ <Rules>
+ <Rule Name="AccessModifierMustBeDeclared">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldsMustBePrivate">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeAnalysisSuppressionMustHaveJustification">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugAssertMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugFailMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ArithmeticExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConditionalExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveDelegateParenthesisWhenPossible">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AttributeConstructorMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveUnnecessaryCode">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
+ <Rules>
+ <Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustAppearInTheCorrectOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeOrderedByAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstantsMustAppearBeforeFields">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticElementsMustAppearBeforeInstanceElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DeclarationKeywordsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ProtectedMustComeBeforeInternal">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertyAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EventAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyElementsMustAppearBeforeStaticNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InstanceReadonlyElementsMustAppearBeforeInstanceNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NoValueFirstComparison">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SystemUsingDirectivesMustBePlacedBeforeOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingDirectivesMustBeOrderedAlphabeticallyByNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingStaticDirectivesMustBePlacedAfterUsingNamespaceDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="CommentsMustContainText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixLocalCallsWithThis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixCallsCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeOnDeclarationLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommaMustBeOnSameLineAsPreviousParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterListMustFollowDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustFollowComma">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustNotSpanMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustFollowPreviousClause">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPlaceRegionsWithinElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainEmptyStatements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedComments">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedRegions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseStringEmptyForEmptyStrings">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseBuiltInTypeAlias">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseShorthandForNullableTypes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.SpacingRules">
+ <Rules>
+ <Rule Name="CommasMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SemicolonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationLinesMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PreprocessorKeywordsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OperatorKeywordMustBeFollowedBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NullableTypeSymbolsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="MemberAccessSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncrementDecrementSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NegativeSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PositiveSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ColonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="TabsMustNotBeUsed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotSplitNullConditionalOperators">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
\ No newline at end of file
--- /dev/null
+using Homescreen.View.Widgets;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ public class TestAddWidget
+ {
+ private AddWidgetPage addWidgetPage;
+ private FastScrollLayout scrollerLayout;
+ private ScrollView scroller;
+ private StackLayout widgetListLayout;
+
+ public void IsNormal()
+ {
+ WidgetsLayout widgetLayout = Program.App.HomeMainPage?.Widgets as WidgetsLayout;
+ WidgetsInformationCenter informationCenter = (widgetLayout as WidgetsLayout)?.BindingContext as WidgetsInformationCenter;
+
+ Assert.That(informationCenter, Is.Not.Null);
+
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Normal));
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.True);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.True);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True);
+ }
+
+ [Test]
+ public async Task SetUp()
+ {
+ HomeMessagingCenter.Send(this, Message.ShowWidgets);
+
+ await Task.Delay(300);
+
+ IsNormal();
+
+ addWidgetPage = new AddWidgetPage() { Title = "Add Widget" };
+ NavigationPage.SetHasBackButton(addWidgetPage, false);
+ await Program.App.HomeMainPage.Navigation.PushAsync(addWidgetPage);
+
+ scrollerLayout = addWidgetPage.ScrollLayout;
+ scroller = addWidgetPage.Scroller;
+ widgetListLayout = addWidgetPage.WidgetListLayout;
+
+ await Task.Delay(200);
+
+ Assert.That(addWidgetPage, Is.Not.Null);
+ Assert.That(scrollerLayout, Is.Not.Null);
+ Assert.That(scroller, Is.Not.Null);
+ Assert.That(widgetListLayout, Is.Not.Null);
+ }
+
+ [Test]
+ public async Task List()
+ {
+ await Task.Delay(300);
+
+ Assert.That(addWidgetPage, Is.Not.Null);
+ Assert.That(scrollerLayout, Is.Not.Null);
+ Assert.That(scroller, Is.Not.Null);
+ Assert.That(widgetListLayout, Is.Not.Null);
+
+ Assert.That(widgetListLayout.Children.Count, Is.EqualTo(5 * 2));
+ Assert.That(widgetListLayout.Height, Is.GreaterThanOrEqualTo(Program.ScreenSize.Height * 1.45));
+ }
+
+ [Test]
+ public async Task ScrollTo()
+ {
+ await Task.Delay(200);
+
+ Assert.That(scroller.ScrollY, Is.EqualTo(0));
+
+ scrollerLayout.IndexSelected?.Invoke(this, new IndexSelectedArgs() { Key = "M" });
+
+ await Task.Delay(300);
+
+ Assert.That(scroller.ScrollY, Is.GreaterThanOrEqualTo(Program.ScreenSize.Height / 2.0));
+
+ scrollerLayout.IndexSelected?.Invoke(this, new IndexSelectedArgs() { Key = "C" });
+
+ await Task.Delay(500);
+
+ Assert.That(scroller.ScrollY, Is.EqualTo(0));
+
+ await Task.Delay(200);
+ }
+
+ [Test]
+ public async Task TearDown()
+ {
+ await Program.App.HomeMainPage.Navigation.PopAsync();
+
+ await Task.Delay(200);
+ }
+ }
+}
--- /dev/null
+using Homescreen.View.Apps;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ public class TestAppsState
+ {
+ private AppsLayout appsLayout;
+ private AppsInformationCenter informationCenter;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ appsLayout = Program.App.HomeMainPage?.Apps as AppsLayout;
+ informationCenter = (appsLayout as AppsLayout)?.BindingContext as AppsInformationCenter;
+
+ HomeMessagingCenter.Send(this, Message.ShowApps);
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ HomeMessagingCenter.Send(this, Message.ShowWidgets);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ appsLayout.SetState(appsLayout.GetNormalState());
+ }
+
+ public void IsNormal([CallerLineNumber] int line = 0)
+ {
+ Assert.That(informationCenter, Is.Not.Null, line: line);
+ Assert.That(informationCenter.AppsState, Is.EqualTo(AppsState.Normal), line: line);
+
+ Assert.That(appsLayout.CurrentState, Is.EqualTo(appsLayout.GetNormalState()), line: line);
+ Assert.That(appsLayout.MenuButton.IsVisible, Is.True, line: line);
+ Assert.That(appsLayout.WidgetsButton.IsVisible, Is.True, line: line);
+ Assert.That(appsLayout.PageScroller.IsVisible, Is.True, line: line);
+ }
+
+ public void IsEdit([CallerLineNumber] int line = 0)
+ {
+ Assert.That(informationCenter, Is.Not.Null, line: line);
+ Assert.That(informationCenter.AppsState, Is.EqualTo(AppsState.Edit), line: line);
+
+ Assert.That(appsLayout.CurrentState, Is.EqualTo(appsLayout.GetEditState()), line: line);
+ Assert.That(appsLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.WidgetsButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.PageScroller.IsVisible, Is.True, line: line);
+ }
+
+ public void IsChoose([CallerLineNumber] int line = 0)
+ {
+ Assert.That(informationCenter, Is.Not.Null, line: line);
+ Assert.That(informationCenter.AppsState, Is.EqualTo(AppsState.Choose), line: line);
+
+ Assert.That(appsLayout.CurrentState, Is.EqualTo(appsLayout.GetChooseState()), line: line);
+ Assert.That(appsLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.WidgetsButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.PageScroller.IsVisible, Is.True, line: line);
+ }
+
+ public void IsDrag([CallerLineNumber] int line = 0)
+ {
+ Assert.That(informationCenter, Is.Not.Null, line: line);
+ Assert.That(informationCenter.AppsState, Is.EqualTo(AppsState.Drag), line: line);
+
+ Assert.That(appsLayout.CurrentState, Is.EqualTo(appsLayout.GetDragState()), line: line);
+ Assert.That(appsLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.WidgetsButton.IsVisible, Is.False, line: line);
+ Assert.That(appsLayout.PageScroller.IsVisible, Is.True, line: line);
+ }
+
+ [Test]
+ public async Task Normal()
+ {
+ await Task.Delay(500);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task Edit()
+ {
+ await Task.Delay(200);
+
+ IsNormal();
+
+ appsLayout.SetState(appsLayout.GetEditState());
+ await Task.Delay(200);
+
+ IsEdit();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task Drag()
+ {
+ await Task.Delay(200);
+
+ IsNormal();
+
+ appsLayout.SetState(appsLayout.GetDragState());
+ await Task.Delay(200);
+
+ IsDrag();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsEdit();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task Choose()
+ {
+ await Task.Delay(200);
+
+ IsNormal();
+
+ appsLayout.SetState(appsLayout.GetChooseState());
+ await Task.Delay(200);
+
+ IsChoose();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsNormal();
+
+ appsLayout.SetState(appsLayout.GetEditState());
+ await Task.Delay(200);
+
+ IsEdit();
+
+ appsLayout.SetState(appsLayout.GetChooseState());
+ await Task.Delay(200);
+
+ IsChoose();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsEdit();
+
+ appsLayout.OnBackKeyPressed();
+ await Task.Delay(200);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task BindToEdit()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ informationCenter.AppsState = AppsState.Edit;
+ await Task.Delay(200);
+
+ IsEdit();
+ }
+
+ [Test]
+ public async Task BindToNormal()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ informationCenter.AppsState = AppsState.Edit;
+ await Task.Delay(200);
+
+ IsEdit();
+
+ informationCenter.AppsState = AppsState.Normal;
+ await Task.Delay(200);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task BindToDrag()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ informationCenter.AppsState = AppsState.Drag;
+ await Task.Delay(200);
+
+ IsDrag();
+ }
+
+ [Test]
+ public async Task BindToChoose()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ informationCenter.AppsState = AppsState.Choose;
+ await Task.Delay(200);
+
+ IsChoose();
+ }
+ }
+}
--- /dev/null
+using Homescreen.DataModel;
+using Homescreen.Model;
+using Homescreen.Tizen.Mobile;
+using NUnit.Framework;
+using SQLite;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ class DataStorage : HomescreenDataStorage
+ {
+ private SQLitePort sqlite;
+
+ static public string testFileName = "test.db";
+ static public string dbPath = Path.Combine(global::Tizen.Applications.Application.Current.DirectoryInfo.Data, "test.db");
+ static public string dbJournalPath = dbPath + "-journal";
+
+ public DataStorage()
+ {
+ sqlite = new SQLitePort();
+ Connect();
+ }
+
+ public void Connect()
+ {
+ connection = sqlite.GetConnection(DataStorage.testFileName);
+ }
+
+ public void Close()
+ {
+ connection.Close();
+ }
+ }
+
+ [TestFixture]
+ public class TestDB
+ {
+ private SQLitePort sqlite;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ sqlite = new SQLitePort();
+
+ if (File.Exists(DataStorage.dbPath))
+ {
+ File.Delete(DataStorage.dbPath);
+ File.Delete(DataStorage.dbJournalPath);
+ }
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ if (File.Exists(DataStorage.dbPath))
+ {
+ File.Delete(DataStorage.dbPath);
+ File.Delete(DataStorage.dbJournalPath);
+ }
+ }
+
+ [Test]
+ public void SQLiteCreateDB()
+ {
+ Assert.That(File.Exists(DataStorage.dbPath), Is.False);
+
+ SQLiteConnection connection = sqlite.GetConnection(DataStorage.testFileName);
+ Assert.That(connection, Is.Not.Null);
+
+ Assert.That(File.Exists(DataStorage.dbPath), Is.True);
+
+ File.Delete(DataStorage.dbPath);
+ File.Delete(DataStorage.dbJournalPath);
+ }
+
+ [Test]
+ public void DataStorageTest()
+ {
+ DataStorage storage = new DataStorage();
+
+ Assert.That(storage, Is.Not.Null);
+
+ Assert.That(File.Exists(DataStorage.dbPath), Is.True);
+
+ WidgetPageCountInformation firstItem = new WidgetPageCountInformation()
+ {
+ PageCount = 1,
+ };
+
+ long firstItemId = storage.Insert<WidgetPageCountInformation>(firstItem);
+
+ IEnumerable<WidgetPageCountInformation> items = storage.Read<WidgetPageCountInformation>();
+
+ Assert.That(items.Count(), Is.EqualTo(1));
+
+ storage.Insert<WidgetPageCountInformation>(new WidgetPageCountInformation()
+ {
+ PageCount = 100,
+ });
+
+ items = storage.Read<WidgetPageCountInformation>();
+
+ Assert.That(items.Count(), Is.EqualTo(2));
+
+ firstItem.PageCount = 2;
+ storage.Update<WidgetPageCountInformation>(firstItem);
+
+ items = storage.Read<WidgetPageCountInformation>();
+
+ Assert.That(items.Count(), Is.EqualTo(2));
+
+ foreach (var item in items)
+ {
+ Assert.That(item.Id, Is.EqualTo(firstItemId));
+ Assert.That(item.PageCount, Is.EqualTo(2));
+ break;
+ }
+
+ storage.Update<WidgetPageCountInformation>(new WidgetPageCountInformation()
+ {
+ PageCount = 300,
+ });
+
+ Assert.That(items.Count(), Is.EqualTo(3));
+
+ storage.Delete<WidgetPageCountInformation>(firstItem);
+
+ Assert.That(items.Count(), Is.EqualTo(2));
+
+ items = storage.Query<WidgetPageCountInformation>("SELECT * FROM WidgetPageCountInformation WHERE PageCount = ?", 100);
+
+ Assert.That(items.Count(), Is.EqualTo(1));
+
+ storage.DeleteAll<WidgetPageCountInformation>();
+
+ items = storage.Read<WidgetPageCountInformation>();
+
+ Assert.That(items.Count(), Is.EqualTo(0));
+
+ storage.Close();
+ }
+ }
+}
--- /dev/null
+using NUnit.Framework;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ public class TestFunction
+ {
+ [Test]
+ public void TestGetPageindex()
+ {
+ for (int pageCount = 1; pageCount <= 100; pageCount++)
+ {
+ for (int index = 0; index < pageCount; index++)
+ {
+ double position = (double)index / pageCount;
+ Assert.That((int)(position * pageCount + 0.5), Is.EqualTo(index));
+ }
+ }
+ }
+ }
+}
--- /dev/null
+using Homescreen.Tizen.Mobile;
+using NUnit.Framework;
+using System.Threading.Tasks;
+using Tizen.System;
+using Xamarin.Forms;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ public class TestWallpaper
+ {
+ private string wallpaperPath;
+ private View.Wallpaper wallpaper;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ wallpaper = Program.App.HomeMainPage?.WallPaper;
+ wallpaperPath = GetPath();
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ WallpaperInfo.IsWrongPath = false;
+ SystemSettings.WallpaperHomeScreen = wallpaperPath;
+ }
+
+ private string GetPath()
+ {
+ return wallpaper?.Source?.GetValue(FileImageSource.FileProperty).ToString();
+ }
+
+ [Test]
+ public void SystemPath()
+ {
+ Assert.That(wallpaper, Is.Not.Null);
+ Assert.That(string.IsNullOrEmpty(wallpaperPath), Is.False);
+ Assert.That(wallpaperPath, Is.EqualTo(SystemSettings.WallpaperHomeScreen));
+ }
+
+ [Test]
+ public async Task WallpaperChanged()
+ {
+ string path_1 = "/opt/usr/data/settings/Wallpapers/home_001.png";
+ string path_2 = "/opt/usr/data/settings/Wallpapers/home_002.png";
+ string path_3 = "/opt/usr/data/settings/Wallpapers/home_003.png";
+ string path_4 = "/opt/usr/data/settings/Wallpapers/home_004.png";
+
+ await Task.Delay(500);
+
+ SystemSettings.WallpaperHomeScreen = path_1;
+ await Task.Delay(500);
+ Assert.That(GetPath(), Is.EqualTo(path_1));
+
+ SystemSettings.WallpaperHomeScreen = path_2;
+ await Task.Delay(500);
+ Assert.That(GetPath(), Is.EqualTo(path_2));
+
+ SystemSettings.WallpaperHomeScreen = path_3;
+ await Task.Delay(500);
+ Assert.That(GetPath(), Is.EqualTo(path_3));
+
+ SystemSettings.WallpaperHomeScreen = path_4;
+ await Task.Delay(500);
+ Assert.That(GetPath(), Is.EqualTo(path_4));
+
+ WallpaperInfo.IsWrongPath = true;
+ SystemSettings.WallpaperHomeScreen = path_2;
+ await Task.Delay(500);
+ Assert.That(GetPath(), Is.EqualTo("default_bg.png"));
+ }
+ }
+}
--- /dev/null
+using Homescreen.Model;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using System;
+using System.Collections.Specialized;
+using Homescreen.Debug;
+using Homescreen.DataModel;
+
+namespace Homescreen.UITest.Tizen.Mobile.Testcase
+{
+ [TestFixture]
+ class TestWidgetsInformationCenter : BindableObject
+ {
+ #region Binding Command
+ public static readonly BindableProperty AddWidgetPageCommandProperty
+ = BindableProperty.Create("AddWidgetPageCommand", typeof(Command), typeof(TestWidgetsInformationCenter));
+
+ public static readonly BindableProperty DeleteWidgetPageCommandProperty
+ = BindableProperty.Create("DeleteWidgetPageCommand", typeof(Command), typeof(TestWidgetsInformationCenter));
+
+ public static readonly BindableProperty AddWidgetCommandProperty
+ = BindableProperty.Create("AddWidgetCommand", typeof(Command), typeof(TestWidgetsInformationCenter));
+
+ public static readonly BindableProperty DeleteWidgetCommandProperty
+ = BindableProperty.Create("DeleteWidgetCommand", typeof(Command), typeof(TestWidgetsInformationCenter));
+
+ public Command AddWidgetPageCommand
+ {
+ get => (Command)GetValue(AddWidgetPageCommandProperty);
+ set => SetValue(AddWidgetPageCommandProperty, value);
+ }
+
+ public Command DeleteWidgetPageCommand
+ {
+ get => (Command)GetValue(DeleteWidgetPageCommandProperty);
+ set => SetValue(DeleteWidgetPageCommandProperty, value);
+ }
+
+ public Command AddWidgetCommand
+ {
+ get => (Command)GetValue(AddWidgetCommandProperty);
+ set => SetValue(AddWidgetCommandProperty, value);
+ }
+
+ public Command DeleteWidgetCommand
+ {
+ get => (Command)GetValue(DeleteWidgetCommandProperty);
+ set => SetValue(DeleteWidgetCommandProperty, value);
+ }
+ #endregion
+
+ public static readonly BindableProperty PageListProperty
+ = BindableProperty.Create("PageList", typeof(ObservableCollection<WidgetPageInformation>),
+ typeof(TestWidgetsInformationCenter), default(ObservableCollection<WidgetPageInformation>), BindingMode.TwoWay);
+
+ public static readonly BindableProperty ScrollPositionProperty = BindableProperty.Create(
+ "ScrollPosition", typeof(double), typeof(TestWidgetsInformationCenter), defaultValue: 0.0, defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(TestWidgetsInformationCenter), defaultValue: 1, defaultBindingMode: BindingMode.TwoWay);
+
+ public ObservableCollection<WidgetPageInformation> PageList
+ {
+ get { return (ObservableCollection<WidgetPageInformation>)GetValue(PageListProperty); }
+ set { SetValue(PageListProperty, value); }
+ }
+
+ public double ScrollPosition
+ {
+ get => (double)GetValue(ScrollPositionProperty);
+ set => SetValue(ScrollPositionProperty, value);
+ }
+
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ private int pageCount;
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == PageListProperty.PropertyName)
+ {
+ pageCount = PageList.Count;
+
+ PageList.CollectionChanged += PageListChanged;
+ }
+ else if (propertyName == PageCountProperty.PropertyName)
+ {
+ pageCount = PageCount;
+ }
+ }
+
+ private void PageListChanged(object sender, NotifyCollectionChangedEventArgs args)
+ {
+ PageCount = PageList.Count;
+
+ pageCount = PageList.Count;
+ }
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ WidgetsInformationCenter center = new WidgetsInformationCenter();
+ BindingContext = center;
+
+ SetBinding(PageListProperty, new Binding { Source = BindingContext, Path = "WidgetsInformation" });
+ SetBinding(ScrollPositionProperty, new Binding { Source = BindingContext, Path = "ScrollPosition" });
+ SetBinding(PageCountProperty, new Binding { Source = BindingContext, Path = "PageCount" });
+ SetBinding(AddWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetPageCommand" });
+ SetBinding(DeleteWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetPageCommand" });
+ SetBinding(AddWidgetCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetCommand" });
+ SetBinding(DeleteWidgetCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetCommand" });
+
+ PageCount = 2;
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ HomeWidgetProvider.Instance.DeleteWidgetAll();
+ HomeWidgetProvider.Instance.SetWidgetPageCount(2);
+ var widget = NewCalendarWidget();
+ widget.PageIndex = 0;
+ HomeWidgetProvider.Instance.InsertWidget(widget);
+ }
+
+ private void TestPageState(int pageCount, int[] widgetCount, int currentPage = 0, [CallerLineNumber] int line = 0)
+ {
+ Assert.That(this.pageCount, Is.EqualTo(pageCount), line: line);
+ Assert.That(PageList.Count, Is.EqualTo(pageCount), line: line);
+ Assert.That(PageCount, Is.EqualTo(pageCount));
+
+ for (int page = 0; page < pageCount; page++)
+ {
+ Assert.That(PageList[page].Widgets.Count, Is.EqualTo(widgetCount[page]), line: line);
+ Assert.That(HomeWidgetProvider.Instance.GetWidgetList(page).Count, Is.EqualTo(widgetCount[page]), line: line);
+ }
+ }
+
+ private WidgetInformation NewCalendarWidget()
+ {
+ return new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = -1,
+ X = 0,
+ Y = 0,
+ };
+ }
+
+ private WidgetInformation NewContactWidget()
+ {
+ return new WidgetInformation()
+ {
+ PackageId = "org.tizen.contacts",
+ Name = "contacts",
+ WidgetId = "org.tizen.contacts.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x2,
+ PageIndex = -1,
+ X = 0,
+ Y = 0,
+ };
+ }
+
+ private async void InitWidget()
+ {
+ int count = PageList.Count;
+ for (int i = 1; i < count; i++)
+ {
+ DeleteWidgetPageCommand.Execute(PageList[1]);
+ await Task.Delay(50);
+ }
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(50);
+
+ List<WidgetInformation> list = new List<WidgetInformation>(PageList[0].Widgets);
+ foreach (var item in list)
+ {
+ DeleteWidgetCommand.Execute(item);
+ await Task.Delay(50);
+ }
+
+ var widget = NewCalendarWidget();
+ widget.PageIndex = 0;
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ PageCount = 2;
+ ScrollPosition = 0;
+ await Task.Delay(50);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ InitWidget();
+ }
+
+ [Test]
+ public async Task BindingPage()
+ {
+ await Task.Delay(300);
+
+ TestPageState(2, new int[] { 1, 0, });
+
+ var center = BindingContext as WidgetsInformationCenter;
+ AddWidgetPageCommand.Execute(null);
+
+ TestPageState(3, new int[] { 1, 0, 0, });
+
+ AddWidgetPageCommand.Execute(null);
+ AddWidgetPageCommand.Execute(null);
+
+ TestPageState(5, new int[] { 1, 0, 0, 0, 0, });
+
+ AddWidgetPageCommand.Execute(null);
+ AddWidgetPageCommand.Execute(null);
+
+ TestPageState(6, new int[] { 1, 0, 0, 0, 0, 0});
+
+ DeleteWidgetPageCommand.Execute(new WidgetPageInformation());
+ DeleteWidgetPageCommand.Execute(PageList[5]);
+
+ TestPageState(5, new int[] { 1, 0, 0, 0, 0, 0 });
+
+ DeleteWidgetPageCommand.Execute(PageList[2]);
+
+ TestPageState(4, new int[] { 1, 0, 0, 0, 0, 0 });
+
+ int index = 0;
+ foreach (var page in PageList)
+ {
+ Assert.That(page.PageIndex, Is.EqualTo(index));
+ index += 1;
+ }
+
+ DeleteWidgetPageCommand.Execute(PageList[0]);
+
+ TestPageState(3, new int[] { 0, 0, 0, 0, 0, 0 });
+
+ DeleteWidgetPageCommand.Execute(PageList[0]);
+ DeleteWidgetPageCommand.Execute(PageList[0]);
+ DeleteWidgetPageCommand.Execute(PageList[0]);
+
+ TestPageState(1, new int[] { 0, 0, 0, 0, 0, 0 });
+ }
+
+ [Test]
+ public async Task BindingWidget()
+ {
+ await Task.Delay(300);
+
+ TestPageState(2, new int[] {1, 0, });
+
+ WidgetInformation widget = NewContactWidget();
+ widget.PageIndex = 0;
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(2, new int[] { 1, 0, });
+
+ widget = NewContactWidget();
+ widget.PageIndex = 1;
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(2, new int[] { 1, 1, });
+
+ widget = NewContactWidget();
+ widget.PageIndex = 1;
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(2, new int[] { 1, 2, });
+
+ AddWidgetPageCommand.Execute(null);
+ AddWidgetPageCommand.Execute(null);
+ AddWidgetPageCommand.Execute(null);
+
+ ScrollPosition = 3.0 / 5.0;
+ await Task.Delay(50);
+
+ widget = NewContactWidget();
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(5, new int[] { 1, 2, 0, 1, 0, }, 3);
+
+ widget = NewContactWidget();
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(5, new int[] { 1, 2, 0, 2, 0, }, 3);
+
+ widget = NewContactWidget();
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(5, new int[] { 1, 2, 1, 2, 0, }, 2);
+
+ widget = NewCalendarWidget();
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(5, new int[] { 1, 2, 1, 2, 1, }, 4);
+
+ widget = NewCalendarWidget();
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(5);
+
+ TestPageState(6, new int[] { 1, 2, 1, 2, 1, 1}, 5);
+ }
+ }
+}
--- /dev/null
+using Homescreen.DataModel;
+using Homescreen.Model;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ class TestProvider : HomeWidgetProvider
+ {
+ public TestProvider()
+ {
+ StorageInstance = new DataStorage();
+ if (StorageInstance.TableExists<WidgetInformation>() == false)
+ {
+ InsertDefaultData();
+ }
+ }
+
+ public void Close()
+ {
+ if (StorageInstance is DataStorage storage)
+ {
+ storage.Close();
+ }
+ }
+ }
+
+ [TestFixture]
+ public class TestWidgetsProvider
+ {
+ TestProvider provider;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ if (File.Exists(DataStorage.dbPath))
+ {
+ File.Delete(DataStorage.dbPath); File.Delete(DataStorage.dbJournalPath);
+ }
+
+ provider = new TestProvider();
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ provider.Close();
+
+ if (File.Exists(DataStorage.dbPath))
+ {
+ File.Delete(DataStorage.dbPath); File.Delete(DataStorage.dbJournalPath);
+ }
+ }
+
+ [Test]
+ public async Task DefaultDataAsync()
+ {
+ await Task.Delay(200);
+
+ Assert.That(provider, Is.Not.Null);
+
+ var pageCount = provider.GetWidgetPageCount();
+
+ Assert.That(pageCount, Is.EqualTo(2));
+
+ var widgetList = provider.GetWidgetList(0);
+ Assert.That(widgetList.Count(), Is.EqualTo(1));
+
+ var item = widgetList[0];
+ Assert.That(item.PackageId, Is.EqualTo("org.tizen.calendar"));
+ Assert.That(item.Name, Is.EqualTo("calendar"));
+ Assert.That(item.WidgetId, Is.EqualTo("org.tizen.calendar.widget"));
+ Assert.That(item.PageIndex, Is.EqualTo(0));
+ Assert.That(item.X, Is.EqualTo(0));
+ Assert.That(item.Y, Is.EqualTo(0));
+
+ widgetList = provider.GetWidgetList(1);
+ Assert.That(widgetList.Count(), Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Page()
+ {
+ provider.SetWidgetPageCount(0);
+ Assert.That(provider.GetWidgetPageCount(), Is.EqualTo(0));
+
+ provider.SetWidgetPageCount(1);
+ Assert.That(provider.GetWidgetPageCount(), Is.EqualTo(1));
+
+ int count = 0;
+ Random r = new Random(unchecked((int)DateTime.Now.Ticks));
+ for (int i = 0; i < 20; i++)
+ {
+ count = r.Next();
+ provider.SetWidgetPageCount(count);
+ Assert.That(provider.GetWidgetPageCount(), Is.EqualTo(count));
+ }
+
+ IEnumerable<WidgetPageCountInformation> list = DataStorage.Instance.Read<WidgetPageCountInformation>();
+ Assert.That(list.Count(), Is.EqualTo(1));
+ }
+
+ [Test]
+ public void Widget()
+ {
+ var widgetList = provider.GetWidgetList(0);
+ Assert.That(widgetList.Count(), Is.EqualTo(1));
+
+ widgetList = provider.GetWidgetList(1);
+ Assert.That(widgetList.Count(), Is.EqualTo(0));
+
+ provider.InsertWidget(new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ PreviewImagePath = "temp",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = 1,
+ X = 0,
+ Y = 0,
+ });
+
+ WidgetInformation widget;
+ provider.InsertWidget(widget = new WidgetInformation()
+ {
+ PackageId = "org.tizen.memo",
+ Name = "Memo",
+ WidgetId = "org.tizen.memo.widget",
+ PreviewImagePath = "temp",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = 2,
+ X = 0,
+ Y = 0,
+ });
+
+ provider.InsertWidget(new WidgetInformation()
+ {
+ PackageId = "org.tizen.contact",
+ Name = "Contact",
+ WidgetId = "org.tizen.contact.widget",
+ PreviewImagePath = "temp",
+ Type = WidgetInformation.SizeType.SIZE_4x2,
+ PageIndex = 4,
+ X = 0,
+ Y = 0,
+ });
+
+ provider.InsertWidget(new WidgetInformation()
+ {
+ PackageId = "org.tizen.contact",
+ Name = "Contact",
+ WidgetId = "org.tizen.contact.widget",
+ PreviewImagePath = "temp",
+ Type = WidgetInformation.SizeType.SIZE_4x2,
+ PageIndex = 4,
+ X = 0,
+ Y = 2,
+ });
+
+ //widgetList = provider.GetWidgetList(0);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //widgetList = provider.GetWidgetList(1);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //widgetList = provider.GetWidgetList(2);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //widgetList = provider.GetWidgetList(3);
+ //Assert.That(widgetList.Count(), Is.EqualTo(0));
+ //widgetList = provider.GetWidgetList(4);
+ //Assert.That(widgetList.Count(), Is.EqualTo(2));
+ //widgetList = provider.GetWidgetList(5);
+ //Assert.That(widgetList.Count(), Is.EqualTo(0));
+
+ //provider.DeleteWidget(widget);
+ //widgetList = provider.GetWidgetList(4);
+ //provider.DeleteWidget(widgetList[1]);
+
+ //widgetList = provider.GetWidgetList(0);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //Assert.That(widgetList[0].WidgetId, Is.EqualTo("org.tizen.calendar.widget"));
+
+ //widgetList = provider.GetWidgetList(1);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //Assert.That(widgetList[0].WidgetId, Is.EqualTo("org.tizen.calendar.widget"));
+
+ //widgetList = provider.GetWidgetList(2);
+ //Assert.That(widgetList.Count(), Is.EqualTo(0));
+
+ //widgetList = provider.GetWidgetList(3);
+ //Assert.That(widgetList.Count(), Is.EqualTo(0));
+
+ //widgetList = provider.GetWidgetList(4);
+ //Assert.That(widgetList.Count(), Is.EqualTo(1));
+ //Assert.That(widgetList[0].WidgetId, Is.EqualTo("org.tizen.contact.widget"));
+
+ //widgetList = provider.GetWidgetList(5);
+ //Assert.That(widgetList.Count(), Is.EqualTo(0));
+ }
+ }
+}
--- /dev/null
+using Homescreen.View;
+using Homescreen.View.Widgets;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ public class TestWidgetsState
+ {
+ private WidgetsLayout widgetLayout;
+ private WidgetsInformationCenter informationCenter;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ widgetLayout = Program.App.HomeMainPage?.Widgets as WidgetsLayout;
+ informationCenter = (widgetLayout as WidgetsLayout)?.BindingContext as WidgetsInformationCenter;
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ widgetLayout.SetState(WidgetsState.Normal);
+ }
+
+ public void IsNormal([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Normal), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.True, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.True, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+
+ foreach (WidgetPageLayout page in widgetLayout.PageScroller.WidgetPageContainer.Children)
+ {
+ Assert.That(page.WidgetBox.Scale, Is.EqualTo(1.0), line: line);
+ foreach (WidgetLayout widget in page.WidgetBox.Children)
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.False, line: line);
+ }
+ }
+ }
+
+ public void IsEdit([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Edit), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+
+ foreach (WidgetPageLayout page in widgetLayout.PageScroller.WidgetPageContainer.Children)
+ {
+ Assert.That(page.WidgetBox.Scale, Is.EqualTo(0.9), line: line);
+ foreach (WidgetLayout widget in page.WidgetBox.Children)
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.True, line: line);
+ }
+ }
+ }
+
+ public void IsAllpage([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Allpage), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.False, line: line);
+
+ AllpageState allPageLayout = widgetLayout.CurrentState as AllpageState;
+ Assert.That(allPageLayout, Is.Not.Null);
+
+ var layout = widgetLayout.AllpageLayout;
+ Assert.That(layout.Children.Count, Is.EqualTo(informationCenter.PageCount >= 6 ? 6 : informationCenter.PageCount + 1));
+ }
+
+ public void IsReorder([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Reorder), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+
+ foreach (WidgetPageLayout page in widgetLayout.PageScroller.WidgetPageContainer.Children)
+ {
+ Assert.That(page.WidgetBox.Scale, Is.EqualTo(0.9), line: line);
+ foreach (WidgetLayout widget in page.WidgetBox.Children)
+ {
+ if (widget.IsPicked)
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.False, line: line);
+ }
+ else
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.True, line: line);
+ }
+ }
+ }
+ }
+
+ public void IsPage(int index, [CallerLineNumber] int line = 0)
+ {
+ var scroller = widgetLayout.PageScroller;
+ var scrollView = widgetLayout.PageScroller.WidgetsScrollView;
+ int pageCount = scroller.PageCount;
+
+ double position = (double)(DeviceInfo.Instance.Width * index) / (DeviceInfo.Instance.Width * pageCount);
+ Assert.That(scroller.ScrollPosition, Is.InRange(position * 0.99, position * 1.01), line: line);
+ Assert.That(scrollView.ScrollX, Is.InRange(DeviceInfo.Instance.Width * index * 0.99, DeviceInfo.Instance.Width * index * 1.01), line: line);
+
+ var indicator = scroller.PageIndicator;
+ var pageContainer = scroller.WidgetPageContainer;
+ for (int idx = 0; idx < pageCount; idx++)
+ {
+ Assert.That(indicator.Children[idx].Rotation, Is.EqualTo(idx == index ? 90 : 0), line: line);
+ Assert.That(indicator.Children[idx].Opacity, Is.EqualTo(idx == index ? 1.0 : 0.3), line: line);
+
+ var page = pageContainer.Children.ElementAt(idx) as WidgetPageLayout;
+ Assert.That(page, Is.Not.Null, line: line);
+ Assert.That(page.PageIndex, Is.EqualTo(idx), line: line);
+ }
+ }
+
+ [Test]
+ public async Task Normal()
+ {
+ await Task.Delay(300);
+ IsNormal();
+
+ widgetLayout.SetState(WidgetsState.Reorder);
+ await Task.Delay(100);
+ IsNormal();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(100);
+ IsNormal();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(100);
+ IsNormal();
+
+ widgetLayout.PageScroller.ScrollTo(1);
+ await Task.Delay(350);
+ IsPage(1);
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(450);
+ IsPage(0);
+ }
+
+ [Test]
+ public async Task Edit()
+ {
+ await Task.Delay(300);
+ IsNormal();
+
+ widgetLayout.SetState(WidgetsState.Edit);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.SetState(WidgetsState.Allpage);
+ await Task.Delay(100);
+ IsEdit();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(350);
+ IsNormal();
+
+ widgetLayout.PageScroller.ScrollTo(1);
+ await Task.Delay(400);
+ IsPage(1);
+
+ widgetLayout.SetState(WidgetsState.Edit);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(350);
+ IsNormal();
+ IsPage(1);
+
+ widgetLayout.SetState(WidgetsState.Edit);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(400);
+ IsNormal();
+ IsPage(0);
+
+ HomeScrollView scrollView = widgetLayout.PageScroller.WidgetsScrollView;
+ scrollView.MouseHold?.Invoke(scrollView, EventArgs.Empty);
+ await Task.Delay(350);
+ IsEdit();
+
+ scrollView.MouseHold?.Invoke(scrollView, EventArgs.Empty);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(400);
+ IsNormal();
+ IsPage(0);
+
+ scrollView.MouseHold?.Invoke(scrollView, EventArgs.Empty);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(350);
+ IsNormal();
+ }
+
+ [Test]
+ public async Task Reorder()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ widgetLayout.SetState(WidgetsState.Reorder);
+ await Task.Delay(100);
+ IsNormal();
+
+ widgetLayout.SetState(WidgetsState.Edit, EditState.StateOption.PickedUpWidget);
+ await Task.Delay(350);
+ IsReorder();
+
+ widgetLayout.SetState(WidgetsState.Normal, NormalState.StateOption.ToHome);
+ await Task.Delay(100);
+ IsReorder();
+
+ widgetLayout.SetState(WidgetsState.Allpage);
+ await Task.Delay(100);
+ IsReorder();
+
+ widgetLayout.SetState(WidgetsState.Edit);
+ await Task.Delay(350);
+ IsEdit();
+
+ widgetLayout.SetState(WidgetsState.Reorder);
+ await Task.Delay(350);
+ IsReorder();
+
+ widgetLayout.SetState(WidgetsState.Edit, EditState.StateOption.ToHome);
+ await Task.Delay(350);
+ IsNormal();
+
+ WidgetLayout widget = (widgetLayout.PageScroller.WidgetPageContainer.Children[0] as WidgetPageLayout)?.WidgetBox.Children[0] as WidgetLayout;
+ Assert.That(widget, Is.Not.Null);
+
+ widget.MouseHold?.Invoke(widget, EventArgs.Empty);
+ await Task.Delay(350);
+ IsReorder();
+
+ widget.MouseUp?.Invoke(widget, new MouseEventArgs
+ {
+ Down = new Xamarin.Forms.Point { X = 100, Y = 100 },
+ Current = new Xamarin.Forms.Point { X = 100, Y = 100 },
+ });
+ await Task.Delay(500);
+ IsEdit();
+
+ widget.MouseHold?.Invoke(widget, EventArgs.Empty);
+ await Task.Delay(350);
+ IsReorder();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(350);
+ IsEdit();
+
+ widget.MouseHold?.Invoke(widget, EventArgs.Empty);
+ await Task.Delay(350);
+ IsReorder();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(350);
+
+ IsNormal();
+
+ widget.MouseHold?.Invoke(widget, EventArgs.Empty);
+ await Task.Delay(350);
+ IsReorder();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(500);
+
+ IsNormal();
+ }
+
+ [Test]
+ public async Task Allpage()
+ {
+ await Task.Delay(300);
+
+ IsNormal();
+
+ widgetLayout.SetState(WidgetsState.Allpage);
+ await Task.Delay(200);
+ IsAllpage();
+
+ widgetLayout.SetState(WidgetsState.Edit);
+ await Task.Delay(100);
+ IsAllpage();
+
+ widgetLayout.SetState(WidgetsState.Reorder);
+ await Task.Delay(100);
+ IsAllpage();
+
+ widgetLayout.OnBackKeyPressed();
+ await Task.Delay(300);
+
+ IsNormal();
+
+ widgetLayout.PageScroller.ScrollTo(1);
+ await Task.Delay(350);
+ IsPage(1);
+
+ widgetLayout.SetState(WidgetsState.Allpage);
+ await Task.Delay(200);
+ IsAllpage();
+
+ widgetLayout.OnHomeKeyPressed();
+ await Task.Delay(500);
+ IsPage(0);
+ }
+ }
+}
--- /dev/null
+using Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View;
+using Homescreen.View.Widgets;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.UITest.Tizen.Mobile
+{
+ [TestFixture]
+ class TestWidgetsUIEdit : BindableObject
+ {
+ #region Binding Command
+ public static readonly BindableProperty AddWidgetPageCommandProperty
+ = BindableProperty.Create("AddWidgetPageCommand", typeof(Command), typeof(TestWidgetsUIEdit));
+
+ public static readonly BindableProperty DeleteWidgetPageCommandProperty
+ = BindableProperty.Create("DeleteWidgetPageCommand", typeof(Command), typeof(TestWidgetsUIEdit));
+
+ public static readonly BindableProperty AddWidgetCommandProperty
+ = BindableProperty.Create("AddWidgetCommand", typeof(Command), typeof(TestWidgetsUIEdit));
+
+ public static readonly BindableProperty DeleteWidgetCommandProperty
+ = BindableProperty.Create("DeleteWidgetCommand", typeof(Command), typeof(TestWidgetsUIEdit));
+
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(TestWidgetsUIEdit));
+
+ public Command AddWidgetPageCommand
+ {
+ get => (Command)GetValue(AddWidgetPageCommandProperty);
+ set => SetValue(AddWidgetPageCommandProperty, value);
+ }
+
+ public Command DeleteWidgetPageCommand
+ {
+ get => (Command)GetValue(DeleteWidgetPageCommandProperty);
+ set => SetValue(DeleteWidgetPageCommandProperty, value);
+ }
+
+ public Command AddWidgetCommand
+ {
+ get => (Command)GetValue(AddWidgetCommandProperty);
+ set => SetValue(AddWidgetCommandProperty, value);
+ }
+
+ public Command DeleteWidgetCommand
+ {
+ get => (Command)GetValue(DeleteWidgetCommandProperty);
+ set => SetValue(DeleteWidgetCommandProperty, value);
+ }
+
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+ #endregion
+
+ private WidgetsInformationCenter informationCenter;
+ private WidgetsLayout widgetLayout;
+ private WidgetPageScrollView scroller;
+ private HomeScrollView scrollView;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ widgetLayout = Program.App.HomeMainPage?.Widgets as WidgetsLayout;
+ scroller = widgetLayout.PageScroller;
+ scrollView = scroller.WidgetsScrollView;
+
+ informationCenter = (widgetLayout as WidgetsLayout)?.BindingContext as WidgetsInformationCenter;
+
+ BindingContext = informationCenter;
+
+ SetBinding(AddWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetPageCommand" });
+ SetBinding(DeleteWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetPageCommand" });
+ SetBinding(AddWidgetCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetCommand" });
+ SetBinding(DeleteWidgetCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetCommand" });
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ var widget = informationCenter.WidgetsInformation[0].Widgets[0];
+ widget.Id = HomeWidgetProvider.Instance.GetWidgetList(0)[0].Id;
+
+ Device.StartTimer(System.TimeSpan.FromMilliseconds(1000), () =>
+ {
+ RemoteViewStorage.Print("TUnit");
+ return false;
+ });
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ SetDefault();
+
+ widgetLayout.SetState(WidgetsState.Normal);
+ }
+
+ private async void SetDefault()
+ {
+ int count = scroller.PageCount;
+ for (int i = 1; i < count; i++)
+ {
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(50);
+ }
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(50);
+
+ List<WidgetInformation> list = new List<WidgetInformation>(informationCenter.WidgetsInformation[0].Widgets);
+ foreach (var item in list)
+ {
+ DeleteWidgetCommand.Execute(item);
+ await Task.Delay(50);
+ }
+
+ var widget = new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = 0,
+ X = 0,
+ Y = 0,
+ };
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(50);
+ }
+
+ public void IsEdit([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Edit), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+
+ foreach (WidgetPageLayout page in widgetLayout.PageScroller.WidgetPageContainer.Children)
+ {
+ Assert.That(page.WidgetBox.Scale, Is.EqualTo(0.9), line: line);
+ foreach (WidgetLayout widget in page.WidgetBox.Children)
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.True, line: line);
+ Assert.That(widget.BackgroundColor, Is.EqualTo(new Color(0, 0, 0, 0.1)), line: line);
+ }
+ }
+ }
+
+ public void IsReorder([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Reorder), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.False, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+
+ foreach (WidgetPageLayout page in widgetLayout.PageScroller.WidgetPageContainer.Children)
+ {
+ Assert.That(page.WidgetBox.Scale, Is.EqualTo(0.9), line: line);
+ foreach (WidgetLayout widget in page.WidgetBox.Children)
+ {
+ Assert.That(widget.WidgetDeleteButton.IsVisible, Is.True, line: line);
+ Assert.That(widget.BackgroundColor, Is.EqualTo(new Color(0, 0, 0, 0.1)), line: line);
+ }
+ }
+ }
+
+ private int GetLineNumber([CallerLineNumber] int line = 0)
+ {
+ return line;
+ }
+
+ public void CheckUI(int pageIndex, [CallerLineNumber] int line = 0)
+ {
+ line *= 10000;
+
+ int pageCount = informationCenter.PageCount;
+
+ double position = (double)(DeviceInfo.Instance.Width * pageIndex) / (DeviceInfo.Instance.Width * pageCount);
+ Assert.That(scroller.ScrollPosition, Is.InRange(position * 0.99, position * 1.01), line: line + GetLineNumber());
+ Assert.That(scrollView.ScrollX, Is.InRange(DeviceInfo.Instance.Width * pageIndex * 0.99, DeviceInfo.Instance.Width * pageIndex * 1.01), line: line);
+
+ var pageLayout = scroller.WidgetPageContainer.Children[pageIndex] as WidgetPageLayout;
+ Assert.That(pageLayout.PageIndex, Is.EqualTo(pageIndex), line: line + GetLineNumber());
+ Assert.That(pageLayout.WidgetBox.Scale, Is.EqualTo(0.9), line: line + GetLineNumber());
+ Assert.That(pageLayout.WidgetBoxBG.IsVisible, Is.True, line: line + GetLineNumber());
+ Assert.That(pageLayout.WidgetBoxBG.Scale, Is.EqualTo(0.9), line: line + GetLineNumber());
+ Assert.That(pageLayout.WidgetBoxBG.Opacity, Is.EqualTo(0.1), line: line + GetLineNumber());
+
+ foreach (WidgetLayout widgetLayout in pageLayout.WidgetBox.Children)
+ {
+ Assert.That(widgetLayout.WidgetDeleteButton.IsVisible, Is.True, line: line + GetLineNumber());
+ Assert.That(widgetLayout.WidgetDeleteButton.X, Is.InRange(DeviceInfo.Instance.Width * 0.012, DeviceInfo.Instance.Width * 0.016), line: line + GetLineNumber());
+ Assert.That(widgetLayout.WidgetDeleteButton.Y, Is.InRange(DeviceInfo.Instance.Width * 0.012, DeviceInfo.Instance.Width * 0.016), line: line + GetLineNumber());
+ Assert.That(widgetLayout.WidgetDeleteButton.Width, Is.InRange(DeviceInfo.Instance.Width * 0.077, DeviceInfo.Instance.Width * 0.084), line: line + GetLineNumber());
+ Assert.That(widgetLayout.WidgetDeleteButton.Height, Is.InRange(DeviceInfo.Instance.Width * 0.077, DeviceInfo.Instance.Width * 0.084), line: line + GetLineNumber());
+ }
+
+ var indicator = scroller.PageIndicator;
+ var pageContainer = scroller.WidgetPageContainer;
+ for (int idx = 0; idx < pageCount; idx++)
+ {
+ Assert.That(indicator.Children[idx].Rotation, Is.EqualTo(idx == pageIndex ? 90 : 0), line: line + GetLineNumber());
+ Assert.That(indicator.Children[idx].Opacity, Is.EqualTo(idx == pageIndex ? 1.0 : 0.3), line: line + GetLineNumber());
+ }
+ }
+
+ private WidgetInformation NewCalendarWidget()
+ {
+ return new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = -1,
+ X = 0,
+ Y = 0,
+ };
+ }
+
+ private WidgetInformation NewContactWidget()
+ {
+ return new WidgetInformation()
+ {
+ PackageId = "org.tizen.contacts",
+ Name = "contacts",
+ WidgetId = "org.tizen.contacts.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x2,
+ PageIndex = -1,
+ X = 0,
+ Y = 0,
+ };
+ }
+
+ private void DeleteWidget(int pageIndex, int widgetIndex)
+ {
+ var pageLayout = scroller.WidgetPageContainer.Children[pageIndex] as WidgetPageLayout;
+ var widget = pageLayout.WidgetBox.Children[widgetIndex] as WidgetLayout;
+ widget.WidgetDeleteButton.ClickableImage.OnClick();
+ }
+
+ private void PickUp(int pageIndex, int widgetIndex, Point down, Point current)
+ {
+ var pageLayout = scroller.WidgetPageContainer.Children[pageIndex] as WidgetPageLayout;
+ var widget = pageLayout.WidgetBox.Children[widgetIndex] as WidgetLayout;
+ widget.MouseHold.Invoke(widget, new MouseEventArgs { Current = current,Down = down});
+ }
+
+ private void Move(int pageIndex, int widgetIndex, Point down, Point current)
+ {
+ var pageLayout = scroller.WidgetPageContainer.Children[pageIndex] as WidgetPageLayout;
+ var widget = pageLayout.WidgetBox.Children[widgetIndex] as WidgetLayout;
+ widget.MouseMove.Invoke(widget, new MouseEventArgs { Current = current, Down = down });
+ }
+
+ [Test]
+ public async Task Edit()
+ {
+ await Task.Delay(700);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewContactWidget()));
+ await Task.Delay(500);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewContactWidget()));
+ await Task.Delay(500);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewCalendarWidget()));
+ await Task.Delay(500);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+
+ widgetLayout.SetState(WidgetsState.Edit);
+
+ await Task.Delay(650);
+
+ IsEdit();
+
+ CheckUI(0);
+
+ scroller.ScrollTo(1);
+ await Task.Delay(350);
+ CheckUI(1);
+
+ scroller.ScrollTo(2);
+ await Task.Delay(350);
+ CheckUI(2);
+
+ DeleteWidget(2, 0);
+ await Task.Delay(350);
+ CheckUI(2);
+
+ scroller.ScrollTo(1);
+ await Task.Delay(350);
+ DeleteWidget(1, 0);
+ await Task.Delay(350);
+ CheckUI(1);
+ DeleteWidget(1, 0);
+ await Task.Delay(350);
+ CheckUI(1);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+ DeleteWidget(0, 0);
+ await Task.Delay(350);
+ CheckUI(0);
+
+ scroller.ScrollTo(1);
+ await Task.Delay(350);
+ CheckUI(1);
+
+ scroller.ScrollTo(2);
+ await Task.Delay(350);
+ CheckUI(2);
+ }
+
+ [Test]
+ public async Task Reorder()
+ {
+ await Task.Delay(700);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewContactWidget()));
+ await Task.Delay(500);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewContactWidget()));
+ await Task.Delay(500);
+
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(NewCalendarWidget()));
+ await Task.Delay(500);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+
+ widgetLayout.SetState(WidgetsState.Edit);
+
+ await Task.Delay(650);
+
+ IsEdit();
+
+ CheckUI(0);
+
+ Point current = new Xamarin.Forms.Point { X = DeviceInfo.Instance.Width * 0.5, Y = DeviceInfo.Instance.Height * 0.3 };
+ Point down = new Xamarin.Forms.Point { X = DeviceInfo.Instance.Width * 0.5, Y = DeviceInfo.Instance.Height * 0.3 };
+
+ PickUp(0, 0, down, current);
+ await Task.Delay(100);
+
+ IsReorder();
+
+ current.X = DeviceInfo.Instance.Width * 0.7;
+ current.Y = DeviceInfo.Instance.Height * 0.5;
+ Move(0, 0, down, current);
+ await Task.Delay(100);
+
+ await Task.Delay(3000);
+
+ widgetLayout.SetState(WidgetsState.Edit);
+
+ await Task.Delay(350);
+ }
+ }
+}
--- /dev/null
+using Homescreen.DataModel;
+using Homescreen.Model;
+using Homescreen.View;
+using Homescreen.View.Widgets;
+using Homescreen.ViewModel;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.UITest.Tizen.Mobile.Testcase
+{
+ [TestFixture]
+ class TestWidgetsUINormal : BindableObject
+ {
+ #region Binding Command
+ public static readonly BindableProperty AddWidgetPageCommandProperty
+ = BindableProperty.Create("AddWidgetPageCommand", typeof(Command), typeof(TestWidgetsUINormal));
+
+ public static readonly BindableProperty DeleteWidgetPageCommandProperty
+ = BindableProperty.Create("DeleteWidgetPageCommand", typeof(Command), typeof(TestWidgetsUINormal));
+
+ public static readonly BindableProperty AddWidgetCommandProperty
+ = BindableProperty.Create("AddWidgetCommand", typeof(Command), typeof(TestWidgetsUINormal));
+
+ public static readonly BindableProperty DeleteWidgetCommandProperty
+ = BindableProperty.Create("DeleteWidgetCommand", typeof(Command), typeof(TestWidgetsUINormal));
+
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(TestWidgetsUINormal));
+
+ public Command AddWidgetPageCommand
+ {
+ get => (Command)GetValue(AddWidgetPageCommandProperty);
+ set => SetValue(AddWidgetPageCommandProperty, value);
+ }
+
+ public Command DeleteWidgetPageCommand
+ {
+ get => (Command)GetValue(DeleteWidgetPageCommandProperty);
+ set => SetValue(DeleteWidgetPageCommandProperty, value);
+ }
+
+ public Command AddWidgetCommand
+ {
+ get => (Command)GetValue(AddWidgetCommandProperty);
+ set => SetValue(AddWidgetCommandProperty, value);
+ }
+
+ public Command DeleteWidgetCommand
+ {
+ get => (Command)GetValue(DeleteWidgetCommandProperty);
+ set => SetValue(DeleteWidgetCommandProperty, value);
+ }
+
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+ #endregion
+
+ private WidgetsLayout widgetLayout;
+ private WidgetsInformationCenter informationCenter;
+ private WidgetPageScrollView scroller;
+ private HomeScrollView scrollView;
+ private OptionButton menuButton;
+ private OptionButton appsButton;
+ private AddWidgetPage addWidgetPage;
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ widgetLayout = Program.App.HomeMainPage?.Widgets as WidgetsLayout;
+ scroller = widgetLayout.PageScroller;
+ scrollView = scroller.WidgetsScrollView;
+ menuButton = widgetLayout.MenuButton;
+ appsButton = widgetLayout.AppsButton;
+
+ informationCenter = (widgetLayout as WidgetsLayout)?.BindingContext as WidgetsInformationCenter;
+
+ var widget = informationCenter.WidgetsInformation[0].Widgets[0];
+ widget.Id = HomeWidgetProvider.Instance.GetWidgetList(0)[0].Id;
+
+ BindingContext = informationCenter;
+
+ SetBinding(AddWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetPageCommand" });
+ SetBinding(DeleteWidgetPageCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetPageCommand" });
+ SetBinding(AddWidgetCommandProperty, new Binding { Source = BindingContext, Path = "AddWidgetCommand" });
+ SetBinding(DeleteWidgetCommandProperty, new Binding { Source = BindingContext, Path = "DeleteWidgetCommand" });
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ var widget = informationCenter.WidgetsInformation[0].Widgets[0];
+ widget.Id = HomeWidgetProvider.Instance.GetWidgetList(0)[0].Id;
+ }
+
+ public void IsNormal([CallerLineNumber] int line = 0)
+ {
+ Assert.That(widgetLayout.CurrentState.State, Is.EqualTo(WidgetsState.Normal), line: line);
+ Assert.That(widgetLayout.MenuButton.IsVisible, Is.True, line: line);
+ Assert.That(widgetLayout.AppsButton.IsVisible, Is.True, line: line);
+ Assert.That(widgetLayout.PageScroller.IsVisible, Is.True, line: line);
+ }
+
+ public void IsPage(int index, [CallerLineNumber] int line = 0)
+ {
+ int pageCount = scroller.PageCount;
+
+ double position = (double)(DeviceInfo.Instance.Width * index) / (DeviceInfo.Instance.Width * pageCount);
+ Assert.That(scroller.ScrollPosition, Is.InRange(position * 0.99, position * 1.01), line: line);
+ Assert.That(scrollView.ScrollX, Is.InRange(DeviceInfo.Instance.Width * index * 0.99, DeviceInfo.Instance.Width * index * 1.01), line: line);
+
+ var indicator = scroller.PageIndicator;
+ var pageContainer = scroller.WidgetPageContainer;
+ for (int idx = 0; idx < pageCount; idx++)
+ {
+ Assert.That(indicator.Children[idx].Rotation, Is.EqualTo(idx == index ? 90 : 0), line: line);
+ Assert.That(indicator.Children[idx].Opacity, Is.EqualTo(idx == index ? 1.0 : 0.3), line: line);
+
+ var page = pageContainer.Children.ElementAt(idx) as WidgetPageLayout;
+ Assert.That(page, Is.Not.Null, line: line);
+ Assert.That(page.PageIndex, Is.EqualTo(idx), line: line);
+ }
+ }
+
+ public void IsDefault([CallerLineNumber] int line = 0)
+ {
+ Assert.That(HomeWidgetProvider.Instance.GetWidgetPageCount(), Is.EqualTo(2), line: line);
+ var list = HomeWidgetProvider.Instance.GetWidgetList(0);
+ Assert.That(list.Count, Is.EqualTo(1), line: line);
+ Assert.That(list[0].Id, Is.EqualTo(informationCenter.WidgetsInformation[0].Widgets[0].Id), line: line);
+ Assert.That(HomeWidgetProvider.Instance.GetWidgetList(1).Count, Is.EqualTo(0), line: line);
+
+ Assert.That(menuButton.X, Is.GreaterThanOrEqualTo(DeviceInfo.Instance.Width * 0.04), line: line);
+ Assert.That(menuButton.X + menuButton.Width, Is.LessThanOrEqualTo(DeviceInfo.Instance.Width * 0.23), line: line);
+ Assert.That(menuButton.Y, Is.GreaterThanOrEqualTo(DeviceInfo.Instance.Height * 0.92), line: line);
+ Assert.That(menuButton.Y + menuButton.Height, Is.LessThanOrEqualTo(DeviceInfo.Instance.Height * 0.99), line: line);
+
+ Assert.That(appsButton.X, Is.GreaterThanOrEqualTo(DeviceInfo.Instance.Width * 0.77), line: line);
+ Assert.That(appsButton.X + menuButton.Width, Is.LessThanOrEqualTo(DeviceInfo.Instance.Width * 0.96), line: line);
+ Assert.That(appsButton.Y, Is.GreaterThanOrEqualTo(DeviceInfo.Instance.Height * 0.92), line: line);
+ Assert.That(appsButton.Y + menuButton.Height, Is.LessThanOrEqualTo(DeviceInfo.Instance.Height * 0.99), line: line);
+
+ Assert.That(scroller.PageCount, Is.EqualTo(2), line: line);
+
+ IsPage(0, line: line);
+
+ var indicator = scroller.PageIndicator;
+ double centerX = (indicator.X + indicator.X + indicator.Width) / 2.0;
+ double centerY = (indicator.Y + indicator.Y + indicator.Height) / 2.0;
+
+ Assert.That(centerX, Is.InRange(DeviceInfo.Instance.Width * 0.49, DeviceInfo.Instance.Height * 0.51), line: line);
+ Assert.That(centerY, Is.InRange(DeviceInfo.Instance.Height * 0.95, DeviceInfo.Instance.Height * 0.97), line: line);
+
+ var pageContainer = scroller.WidgetPageContainer;
+ Assert.That(pageContainer.Children.Count, Is.EqualTo(2), line: line);
+
+ var page = pageContainer.Children.ElementAt(0) as WidgetPageLayout;
+ Assert.That(page, Is.Not.Null, line: line);
+ Assert.That(page.WidgetBox.X, Is.InRange(DeviceInfo.Instance.Width * 0.008, DeviceInfo.Instance.Width * 0.009), line: line);
+ Assert.That(page.WidgetBox.Y, Is.InRange(DeviceInfo.Instance.Height * 0.085, DeviceInfo.Instance.Height * 0.087), line: line);
+ Assert.That(page.WidgetBox.Width, Is.InRange(DeviceInfo.Instance.Width * 0.98, DeviceInfo.Instance.Width * 0.99), line: line);
+ Assert.That(page.WidgetBox.Height, Is.InRange(DeviceInfo.Instance.Height * 0.55, DeviceInfo.Instance.Height * 0.57), line: line);
+ Assert.That(page.WidgetBox.Children.Count, Is.EqualTo(1), line: line);
+
+ var widget = page.WidgetBox.Children.ElementAt(0) as WidgetLayout;
+ Assert.That(widget, Is.Not.Null, line: line);
+ Assert.That(widget.WidgetInfo, Is.Not.Null, line: line);
+ Assert.That(widget.WidgetInfo.PageIndex, Is.EqualTo(0), line: line);
+ Assert.That(widget.WidgetInfo.X, Is.EqualTo(0), line: line);
+ Assert.That(widget.WidgetInfo.Y, Is.EqualTo(0), line: line);
+ Assert.That(widget.WidgetInfo.Type, Is.EqualTo(WidgetInformation.SizeType.SIZE_4x4), line: line);
+ Assert.That(widget.WidgetInfo.PackageId.Contains("calendar"), Is.True, line: line);
+
+ page = pageContainer.Children.ElementAt(1) as WidgetPageLayout;
+ Assert.That(page, Is.Not.Null, line: line);
+ Assert.That(page.PageIndex, Is.EqualTo(1), line: line);
+ Assert.That(page.WidgetBox.Children.Count, Is.EqualTo(0), line: line);
+ }
+
+ private async void SetDefault()
+ {
+ int count = scroller.PageCount;
+ for (int i = 1; i < count; i++)
+ {
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(50);
+ }
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(50);
+
+ List<WidgetInformation> list = new List<WidgetInformation>(informationCenter.WidgetsInformation[0].Widgets);
+ foreach (var item in list)
+ {
+ DeleteWidgetCommand.Execute(item);
+ await Task.Delay(50);
+ }
+
+ var widget = new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = 0,
+ X = 0,
+ Y = 0,
+ };
+ AddWidgetCommand.Execute(new WidgetInformationToAdd(widget));
+
+ await Task.Delay(50);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ SetDefault();
+ }
+
+ [Test]
+ public async Task Default()
+ {
+ await Task.Delay(700);
+
+ IsNormal();
+
+ IsDefault();
+ }
+
+ [Test]
+ public async Task ScrollPage()
+ {
+ await Task.Delay(700);
+
+ IsDefault();
+ /* current page count is 2 */
+
+ scroller.ScrollTo(1);
+ await Task.Delay(450);
+ IsPage(1);
+
+ var unit = scroller.PageIndicator.Children[0] as IndicatorUnit;
+ unit?.Clicked?.Invoke(unit, EventArgs.Empty);
+ await Task.Delay(350);
+ IsPage(0);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 3 */
+
+ scroller.ScrollTo(2);
+ await Task.Delay(450);
+ IsPage(2);
+
+ unit = scroller.PageIndicator.Children[1] as IndicatorUnit;
+ unit?.Clicked?.Invoke(unit, EventArgs.Empty);
+ await Task.Delay(350);
+ IsPage(1);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 4 */
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+ IsPage(0);
+
+ unit = scroller.PageIndicator.Children[3] as IndicatorUnit;
+ unit?.Clicked?.Invoke(unit, EventArgs.Empty);
+ await Task.Delay(350);
+ IsPage(3);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 5 */
+
+ scroller.ScrollTo(4);
+ await Task.Delay(350);
+ IsPage(4);
+
+ unit = scroller.PageIndicator.Children[2] as IndicatorUnit;
+ unit?.Clicked?.Invoke(unit, EventArgs.Empty);
+ await Task.Delay(350);
+ IsPage(2);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 6 */
+
+ scroller.ScrollTo(5);
+ await Task.Delay(350);
+ IsPage(5);
+
+ unit = scroller.PageIndicator.Children[1] as IndicatorUnit;
+ unit?.Clicked?.Invoke(unit, EventArgs.Empty);
+ await Task.Delay(350);
+ IsPage(1);
+ }
+
+ [Test]
+ public async Task DeletePage()
+ {
+ await Task.Delay(700);
+
+ IsDefault();
+ /* current page count is 2 */
+
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(100);
+ /* now page count is 1 */
+
+ IsPage(0);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 2 */
+
+ scroller.ScrollTo(1);
+ await Task.Delay(450);
+ IsPage(1);
+
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(300);
+ /* now page count is 1 */
+
+ IsPage(0);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(50);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(50);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 4 */
+
+ scroller.ScrollTo(3);
+ await Task.Delay(350);
+ IsPage(3);
+
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(100);
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[1]);
+ await Task.Delay(100);
+ /* now page count is 2 */
+
+ scroller.ScrollTo(1);
+ await Task.Delay(350);
+ IsPage(1);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ /* now page count is 6 */
+
+ scroller.ScrollTo(5);
+ await Task.Delay(350);
+ IsPage(5);
+
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[5]);
+ await Task.Delay(100);
+ IsPage(4);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+ IsPage(0);
+ }
+
+ private async void ShowAddWidget()
+ {
+ addWidgetPage = new AddWidgetPage()
+ {
+ BindingContext = widgetLayout.BindingContext,
+ Title = "Add Widget"
+ };
+ NavigationPage.SetHasBackButton(addWidgetPage, false);
+ await Program.App.HomeMainPage.Navigation.PushAsync(addWidgetPage);
+ }
+
+ private void HideAddWidget()
+ {
+ Program.App.HomeMainPage.Navigation.PopAsync();
+ }
+
+ private void AddWidget(int listIndex, int widgetIndex)
+ {
+ var list = addWidgetPage.WidgetListLayout;
+ var item = list.Children[listIndex] as ItemLayout;
+ var preview = item.Children[widgetIndex] as PreviewImage;
+
+ preview.Clicked.Invoke(preview, EventArgs.Empty);
+ }
+
+ [Test]
+ public async Task AddWidget()
+ {
+ await Task.Delay(800);
+
+ IsDefault();
+
+ ShowAddWidget();
+
+ await Task.Delay(300);
+
+ AddWidget(1, 0); /* Calendar */
+
+ await Task.Delay(650);
+ IsPage(1);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(450);
+ IsPage(0);
+
+ ShowAddWidget();
+
+ await Task.Delay(300);
+
+ AddWidget(3, 1); /* Contacts 4 x 2 */
+
+ await Task.Delay(750);
+ IsPage(2);
+
+ ShowAddWidget();
+
+ await Task.Delay(400);
+
+ AddWidget(3, 0); /* Contacts 4 x 4 */
+
+ await Task.Delay(750);
+ IsPage(3);
+
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+ AddWidgetPageCommand.Execute(null);
+ await Task.Delay(100);
+
+ scroller.ScrollTo(5);
+ await Task.Delay(450);
+ IsPage(5);
+
+ ShowAddWidget();
+
+ await Task.Delay(200);
+
+ AddWidget(7, 0); /* Memo */
+
+ await Task.Delay(750);
+ IsPage(5);
+
+ ShowAddWidget();
+
+ await Task.Delay(300);
+
+ AddWidget(9, 0); /* Music player */
+
+ await Task.Delay(750);
+ IsPage(2);
+
+ ShowAddWidget();
+
+ await Task.Delay(300);
+
+ AddWidget(7, 0); /* Memo */
+
+ await Task.Delay(750);
+ IsPage(4);
+
+ scroller.ScrollTo(5);
+ await Task.Delay(450);
+ IsPage(5);
+
+ DeleteWidgetPageCommand.Execute(informationCenter.WidgetsInformation[5]);
+ await Task.Delay(300);
+ IsPage(4);
+
+ scroller.ScrollTo(0);
+ await Task.Delay(350);
+ IsPage(0);
+
+ ShowAddWidget();
+
+ await Task.Delay(300);
+
+ AddWidget(7, 0); /* Memo */
+
+ await Task.Delay(850);
+ IsPage(5);
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.tizen.example.Homescreen.UITest.Tizen.Mobile" version="1.0.0" api-version="4" xmlns="http://tizen.org/ns/packages">
+ <profile name="mobile" />
+ <ui-application appid="org.tizen.example.Homescreen.UITest.Tizen.Mobile" exec="Homescreen.UITest.Tizen.Mobile.dll" multiple="false" nodisplay="false" taskmanage="true" splash-screen-display="true" type="dotnet" launch_mode="single">
+ <label>Homescreen.UITest</label>
+ <icon>Homescreen.UITest.Tizen.Mobile.png</icon>
+ <category name="http://tizen.org/category/homeapp"/>
+ <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+ </ui-application>
+ <shortcut-list />
+ <privileges>
+ <privilege>http://tizen.org/privilege/systemsettings.admin</privilege>
+ <privilege>http://tizen.org/privilege/widget.viewer</privilege>
+ <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
+ <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
+ <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+ <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ <privilege>http://tizen.org/privilege/notification</privilege>
+ <privilege>http://tizen.org/privilege/filesystem.write</privilege>
+ </privileges>
+ <provides-appdefined-privileges />
+</manifest>
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27004.2006
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Homescreen", "Homescreen\Homescreen\Homescreen.csproj", "{EFD8FCA3-9866-4C5E-9FCF-A5E41E4EB797}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Homescreen.Tizen.Mobile", "Homescreen\Homescreen.Tizen.Mobile\Homescreen.Tizen.Mobile.csproj", "{6F65D4D0-3670-4537-BD38-47884B9CF6BE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Homescreen.UITest.Tizen.Mobile", "Homescreen.UITest\Homescreen.UITest.Tizen.Mobile\Homescreen.UITest.Tizen.Mobile.csproj", "{FD0B11A9-2E0C-4F5B-A454-2B82BD85A6DC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {EFD8FCA3-9866-4C5E-9FCF-A5E41E4EB797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EFD8FCA3-9866-4C5E-9FCF-A5E41E4EB797}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EFD8FCA3-9866-4C5E-9FCF-A5E41E4EB797}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EFD8FCA3-9866-4C5E-9FCF-A5E41E4EB797}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6F65D4D0-3670-4537-BD38-47884B9CF6BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6F65D4D0-3670-4537-BD38-47884B9CF6BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6F65D4D0-3670-4537-BD38-47884B9CF6BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6F65D4D0-3670-4537-BD38-47884B9CF6BE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FD0B11A9-2E0C-4F5B-A454-2B82BD85A6DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FD0B11A9-2E0C-4F5B-A454-2B82BD85A6DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FD0B11A9-2E0C-4F5B-A454-2B82BD85A6DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FD0B11A9-2E0C-4F5B-A454-2B82BD85A6DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {12B4D75A-E298-472B-AAEF-4B7D921D31CD}
+ EndGlobalSection
+EndGlobal
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Runtime.CompilerServices;
+
+namespace Homescreen.Debug
+{
+ /// <summary>
+ /// DebugLog provides debugging APIs for the portable library project.
+ /// </summary>
+ public class DebugLog : ILog
+ {
+ private static readonly string TAG = "Homescreen";
+
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Debug(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public void Error(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(tag, message, file, func, line);
+ }
+ }
+
+ /// <summary>
+ /// TizenLog provides debugging APIs for the Tizen specific project.
+ /// </summary>
+ public class TizenLog
+ {
+ private static readonly string TAG = "Homescreen";
+
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Debug(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Debug(tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(TAG, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ static public void Error(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ global::Tizen.Log.Error(tag, message, file, func, line);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+using Homescreen.Debug;
+using Xamarin.Forms;
+using Tizen.Xamarin.Forms.Extension.Renderer;
+using ElmSharp;
+using Homescreen.Tizen.Mobile.Ports;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// Tizen mobile home screen reference application.
+ /// This app provides functions regarding widgets display, widget management,
+ /// app list display, app launch, app remove and wallpaper selection.
+ /// Users can set their own widget pages with various widgets on the home screen in Widgets page.
+ /// Also, browse installed apps, managing apps and run an app from the Apps page.
+ /// </summary>
+ class Program : global::Xamarin.Forms.Platform.Tizen.FormsApplication
+ {
+ /// <summary>
+ /// Device screen size
+ /// </summary>
+ static public ElmSharp.Size ScreenSize { get; private set; }
+
+ /// <summary>
+ /// App's window instance.
+ /// </summary>
+ static public Window Window { get; private set; }
+
+ private App app;
+
+ private static ElmSharp.EcoreEvent<ElmSharp.EcoreKeyEventArgs> KeyUpEvent;
+
+ protected override void OnCreate()
+ {
+ base.OnCreate();
+ LoadApplication(app = new App());
+ MainWindow.AvailableRotations = DisplayRotation.Degree_0;
+ MainWindow.StatusBarMode = StatusBarMode.Translucent;
+
+ RemoteViewStorage.Init(MainWindow);
+
+ Window = MainWindow;
+ ScreenSize = MainWindow.ScreenSize;
+
+ KeyUpEvent = new ElmSharp.EcoreEvent<ElmSharp.EcoreKeyEventArgs>(
+ ElmSharp.EcoreEventType.KeyUp,
+ (s, e, t) =>
+ {
+ return ElmSharp.EcoreKeyEventArgs.Create(s, e, t);
+ });
+
+ KeyUpEvent.On += KeyUpListener;
+ MainWindow.KeyGrab(EvasKeyEventArgs.PlatformMenuButtonName, true);
+ }
+
+ private void KeyUpListener(object sender, EcoreKeyEventArgs e)
+ {
+ if (e.KeyName.CompareTo(ElmSharp.EvasKeyEventArgs.PlatformMenuButtonName) == 0)
+ {
+ app?.MenuKeyPressed();
+ }
+ }
+
+ protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
+ {
+ base.OnAppControlReceived(e);
+
+ if (e.ReceivedAppControl.Operation == AppControlOperations.Default)
+ {
+ if (e.ReceivedAppControl.ExtraData.TryGet("__HOME_OP__", out string value))
+ {
+ if (value == "__LAUNCH_BY_HOME_KEY__")
+ {
+ app?.LaunchByHome();
+ }
+ }
+ }
+ }
+
+ protected override void OnTerminate()
+ {
+ base.OnTerminate();
+
+ RemoteViewFactory.Shutdown();
+ KeyUpEvent.On -= KeyUpListener;
+ MainWindow.KeyUngrab(EvasKeyEventArgs.PlatformHomeButtonName);
+ }
+
+ static void Main(string[] args)
+ {
+ DependencyService.Register<DebugLog>();
+ DependencyService.Register<TizenDeviceInfo>();
+ DependencyService.Register<AppLauncher>();
+ DependencyService.Register<WidgetManager>();
+ DependencyService.Register<AlertPopup>();
+ DependencyService.Register<MenuPopup>();
+ DependencyService.Register<ToastPopup>();
+ DependencyService.Register<RemoteViewStorage>();
+
+ var app = new Program();
+ TizenFormsExtension.Init();
+ global::Xamarin.Forms.Platform.Tizen.Forms.Init(app);
+ app.Run(args);
+
+ app.Dispose();
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <!-- Property Group for Tizen40 Project -->
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>tizen40</TargetFramework>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>None</DebugType>
+ </PropertyGroup>
+ <ItemGroup>
+ <None Remove="Renderer\WidgetLayoutRenderer.cs~RF4f5850e1.TMP" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Folder Include="lib\" />
+ </ItemGroup>
+
+
+ <!-- If solution already has PCL project, will reference -->
+ <ItemGroup>
+ <ProjectReference Include="..\Homescreen\Homescreen.csproj" />
+ </ItemGroup>
+
+
+ <!-- Include Nuget Package for Tizen Project building -->
+ <ItemGroup>
+ <PackageReference Include="Plugin.SQLite" Version="1.0.4" />
+ <PackageReference Include="sqlite-net-base" Version="1.5.166-beta" />
+ <PackageReference Include="sqlite-net-pcl" Version="1.4.118" />
+ <PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.8" />
+ <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.8" />
+ <PackageReference Include="Tizen.NET" Version="5.0.0-preview1-00412">
+ <ExcludeAssets>Runtime</ExcludeAssets>
+ </PackageReference>
+ <PackageReference Include="Tizen.NET.Sdk" Version="1.0.1-pre1" />
+ <PackageReference Include="Tizen.Xamarin.Forms.Extension" Version="2.4.0-v00014" />
+ <PackageReference Include="Xamarin.Forms.Platform.Tizen" Version="2.5.0.77107" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View;
+using Tizen.Xamarin.Forms.Extension;
+using Xamarin.Forms;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// AlertPopup makes a pop-up by given information
+ /// such as a title, a content and buttons.
+ /// Providing methods for setting by following IAlertPopup.
+ /// </summary>
+ public class AlertPopup : IAlertPopup
+ {
+ /// <summary>
+ /// A pop-up title
+ /// </summary>
+ public string Title
+ {
+ get => dialog.Title;
+ set => dialog.Title = value;
+ }
+
+ /// <summary>
+ /// A pop-up content view which will be displayed in a pop-up
+ /// </summary>
+ public Xamarin.Forms.View Content
+ {
+ get => dialog.Content;
+ set => dialog.Content = value;
+ }
+
+ /// <summary>
+ /// First button among three pop-up buttons.
+ /// </summary>
+ public Button FirstButton
+ {
+ get => dialog.Positive;
+ set => dialog.Positive = value;
+ }
+
+ /// <summary>
+ /// Second button among three pop-up buttons inside
+ /// </summary>
+ public Button SecondButton
+ {
+ get => dialog.Neutral;
+ set => dialog.Neutral = value;
+ }
+
+ /// <summary>
+ /// Third button among three pop-up buttons inside
+ /// </summary>
+ public Button ThirdButton
+ {
+ get => dialog.Negative;
+ set => dialog.Negative = value;
+ }
+
+ private Dialog dialog;
+
+ /// <summary>
+ /// A constructor which sets internal event handlers.
+ /// </summary>
+ public AlertPopup()
+ {
+ dialog = new Dialog
+ {
+ HorizontalOption = LayoutOptions.FillAndExpand
+ };
+
+ dialog.BackButtonPressed += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+
+ dialog.OutsideClicked += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+
+ dialog.Hidden += (s, arg) =>
+ {
+ Title = null;
+ Content = null;
+ FirstButton = null;
+ SecondButton = null;
+ ThirdButton = null;
+ };
+ }
+
+ /// <summary>
+ /// Hide a pop-up.
+ /// </summary>
+ public void Hide()
+ {
+ dialog.Hide();
+ }
+
+ /// <summary>
+ /// Show a pop-up.
+ /// </summary>
+ public void Show()
+ {
+ dialog.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.Model.Interface;
+using System;
+using Tizen.Applications;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// An app launching port
+ /// </summary>
+ /// <see cref="IAppLauncher"/>
+ public class AppLauncher : IAppLauncher
+ {
+ /// <summary>
+ /// A method launches an app which matched with the appId.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ public void LaunchApp(string appId)
+ {
+ AppControl handle = new AppControl
+ {
+ ApplicationId = appId
+ };
+ try
+ {
+ AppControl.SendLaunchRequest(handle);
+ }
+ catch (Exception e)
+ {
+ TizenLog.Error("AppControl is failed, " + e.Message);
+ return;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile.Ports;
+using System;
+using Homescreen.Model;
+using Tizen.Applications;
+using Xamarin.Forms;
+using Homescreen.DataModel;
+
+[assembly: Dependency(typeof(BadgeNotifier))]
+namespace Homescreen.Tizen.Mobile.Ports
+{
+ /// <summary>
+ /// BadgeNotifier manages event handler for badge notifications
+ /// co-working with Tizen application framework.
+ /// </summary>
+ public class BadgeNotifier : IBadgeEventNotifier
+ {
+ event EventHandler<BadgeUpdateEventArgs> BadgeEventHandler;
+
+ private void PackageManagerBadgeChanged(object sender, BadgeEventArgs e)
+ {
+ Badge badge = e.Badge;
+ if (e.Reason == BadgeEventArgs.Action.Update)
+ {
+ BadgeInformation badgeInfo = new BadgeInformation()
+ {
+ AppId = badge.AppId,
+ Count = badge.Count,
+ IsVisible = badge.Visible
+ };
+ BadgeEventHandler?.Invoke(this, new BadgeUpdateEventArgs() { UpdatedInformation = badgeInfo });
+ }
+ }
+
+ /// <summary>
+ /// Register an event handler to get updated badge notifications.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A registration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool Register(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ BadgeEventHandler += handler;
+ BadgeControl.Changed += PackageManagerBadgeChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A deregistration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool DeRegister(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ BadgeEventHandler -= handler;
+ BadgeControl.Changed -= PackageManagerBadgeChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Provides badge information.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ /// <returns>A badge information</returns>
+ /// <see cref="BadgeInformation"/>
+ BadgeInformation IBadgeEventNotifier.Get(string appId)
+ {
+ Badge badge;
+ try
+ {
+ badge = BadgeControl.Find(appId);
+
+ }
+ catch
+ {
+ return null;
+ }
+
+ BadgeInformation badgeInfo = new BadgeInformation()
+ {
+ AppId = badge.AppId,
+ Count = badge.Count,
+ IsVisible = badge.Visible
+ };
+
+ return badgeInfo;
+ }
+ }
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View;
+using System.Collections.Generic;
+using Tizen.Xamarin.Forms.Extension;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// An interface to make Pop-up menu.
+ /// </summary>
+ public class MenuPopup : IMenuPopup
+ {
+ private ContextPopup popup;
+ private IDictionary<string, ItemSelected> ItemSelectCallbackList = new Dictionary<string, ItemSelected>();
+ private bool isDismissed;
+
+ /// <summary>
+ /// A menu pop-up dismissed status. True, if pop-up is dismissed.
+ /// </summary>
+ public bool IsDismissed => isDismissed;
+
+ /// <summary>
+ /// A method adds a pop-up menu item.
+ /// </summary>
+ /// <param name="title">A menu title</param>
+ /// <param name="itemSelected">A function will be called if the menu is selected.</param>
+ public void AddMenuItem(string title, ItemSelected itemSelected)
+ {
+ ItemSelectCallbackList.Add(title, itemSelected);
+ }
+
+ /// <summary>
+ /// Hide a pop-up menu.
+ /// </summary>
+ public void Hide()
+ {
+ popup.Dismiss();
+ }
+
+ /// <summary>
+ /// Show a pop-up menu.
+ /// </summary>
+ /// <param name="anchor">A view can be base of pop-up menu.</param>
+ public void Show(Xamarin.Forms.View anchor)
+ {
+ popup = new ContextPopup
+ {
+ DirectionPriorities = new ContextPopupDirectionPriorities(ContextPopupDirection.Down, ContextPopupDirection.Right, ContextPopupDirection.Left, ContextPopupDirection.Up),
+ IsAutoHidingEnabled = true,
+ };
+
+ foreach (var item in ItemSelectCallbackList)
+ {
+ popup.Items.Add(new ContextPopupItem(item.Key));
+ }
+
+ popup.SelectedIndexChanged += (s, e) =>
+ {
+ var title = popup.Items[popup.SelectedIndex]?.Label;
+ if (ItemSelectCallbackList.TryGetValue(title, out ItemSelected itemSelected))
+ {
+ itemSelected();
+ popup.Dismiss();
+ }
+ };
+
+ popup.Dismissed += (s, e) =>
+ {
+ isDismissed = true;
+ ItemSelectCallbackList.Clear();
+ };
+
+ isDismissed = false;
+ popup.Show(anchor);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile;
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Homescreen.Model;
+using System.Threading.Tasks;
+using Tizen.Applications;
+using Homescreen.Debug;
+using Homescreen.DataModel;
+
+[assembly: Dependency(typeof(PackageNotifier))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// PackageNotifier provides APIs related with packages in the device.
+ /// Also it manages given event handlers that will be called if a package is updated
+ /// with the Tizen applications framework.
+ /// </summary>
+ public class PackageNotifier : IPackageChanged
+ {
+
+ event EventHandler<PackageUpdateEventArgs> PackageManagerEventHandler;
+
+ private static String DefaultAppIcon = "default_app_icon.png";
+
+
+ /// <summary>
+ /// Register an event handler to get package modification events
+ /// </summary>
+ /// <param name="handler">An event handler will handle the package modification events</param>
+ /// <returns>A registration status</returns>
+ public bool Register(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ PackageManagerEventHandler += handler;
+ PackageManager.InstallProgressChanged += PackageManagerInstallProgressChanged;
+ PackageManager.UninstallProgressChanged += PackageManagerUninstallProgressChanged;
+ PackageManager.UpdateProgressChanged += PackageManagerUpdateProgressChanged;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler
+ /// </summary>
+ /// <param name="handler">An event handler to be deregistered.</param>
+ /// <returns>A deregistration status</returns>
+ public bool DeRegister(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ PackageManagerEventHandler -= handler;
+ PackageManager.InstallProgressChanged -= PackageManagerInstallProgressChanged;
+ PackageManager.UninstallProgressChanged -= PackageManagerUninstallProgressChanged;
+ PackageManager.UpdateProgressChanged -= PackageManagerUpdateProgressChanged;
+
+ return true;
+ }
+
+ private void PackageManagerInstallProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Installed, });
+ }
+ }
+
+ private void PackageManagerUninstallProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Uninstalled, });
+ }
+ }
+
+ private void PackageManagerUpdateProgressChanged(object sender, PackageManagerEventArgs e)
+ {
+ if (e.State == PackageEventState.Completed)
+ {
+ PackageManagerEventHandler?.Invoke(this, new PackageUpdateEventArgs() { PackageId = e.PackageId, Type = PackageUpdateType.Updated, });
+ }
+ }
+
+ /// <summary>
+ /// Get all installed apps information.
+ /// </summary>
+ /// <returns>An installed app information list</returns>
+ public async Task<IList<InstalledAppInformation>> GetAllInstalledAppInformation()
+ {
+ try
+ {
+ List<InstalledAppInformation> resultList = new List<InstalledAppInformation>();
+ Dictionary<string, string> filters = new Dictionary<string, string>();
+
+ ApplicationInfoFilter filter = new ApplicationInfoFilter();
+ filter.Filter.Add(ApplicationInfoFilter.Keys.NoDisplay, "false");
+ // TODO : It is not supported yet
+ //filter.Filter.Add(ApplicationInfoFilter.Keys.InstalledStorage, ApplicationInfoFilter.Values.InstalledInternal);
+
+ Task<IEnumerable<ApplicationInfo>> task = ApplicationManager.GetInstalledApplicationsAsync(filter);
+
+ IEnumerable<ApplicationInfo> installedList = await task;
+
+ foreach (var appInfo in installedList)
+ {
+ if (appInfo.Label == null ||
+ appInfo.ApplicationId == null)
+ {
+ continue;
+ }
+
+ Package pkgInfo = PackageManager.GetPackage(appInfo.PackageId);
+
+ resultList.Add(new InstalledAppInformation
+ {
+ PackageId = appInfo.PackageId,
+ AppId = appInfo.ApplicationId,
+ Title = appInfo.Label,
+ IconPath = (System.IO.File.Exists(appInfo.IconPath)) ? appInfo.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ IsRemovable = pkgInfo.IsRemovable,
+ });
+ }
+
+ // TODO : It is not supported yet
+ resultList.Sort((InstalledAppInformation a, InstalledAppInformation b) =>
+ {
+ return a.Title.CompareTo(b.Title);
+ });
+#if (false)
+ //filter.Filter.Remove(ApplicationInfoFilter.Keys.InstalledStorage);
+ //filter.Filter.Add(ApplicationInfoFilter.Keys.InstalledStorage, ApplicationInfoFilter.Values.InstalledExternal);
+
+ task = ApplicationManager.GetInstalledApplicationsAsync(filter);
+
+ IEnumerable<ApplicationInfo> installedExternalList = await task;
+
+ foreach (var appInfoForExternal in installedExternalList)
+ {
+
+ if (appInfoForExternal.Label == null ||
+ appInfoForExternal.ApplicationId == null)
+ {
+ continue;
+ }
+
+ resultList.Add(new InstalledAppInformation
+ {
+ PackageId = appInfoForExternal.PackageId,
+ AppId = appInfoForExternal.ApplicationId,
+ Title = appInfoForExternal.Label,
+ IconPath = (System.IO.File.Exists(appInfoForExternal.IconPath)) ? appInfoForExternal.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ });
+ }
+#endif
+ return resultList;
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug(e.Message);
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Get an installed app information matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>A matching app information.</returns>
+ public InstalledAppInformation GetInstalledAppInformation(string applicationId)
+ {
+ ApplicationInfo appInfo = ApplicationManager.GetInstalledApplication(applicationId);
+ Package pkgInfo = PackageManager.GetPackage(appInfo.PackageId);
+
+ InstalledAppInformation newInfo = new InstalledAppInformation()
+ {
+ PackageId = appInfo.PackageId,
+ AppId = appInfo.ApplicationId,
+ Title = appInfo.Label,
+ IconPath = (System.IO.File.Exists(appInfo.IconPath)) ? appInfo.IconPath : DefaultAppIcon,
+ IsInSdCard = false,
+ IsRemovable = pkgInfo.IsRemovable,
+ };
+
+ return newInfo;
+ }
+
+ /// <summary>
+ /// Uninstall an app matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>An uninstall status</returns>
+ public bool RequestUninstall(string applicationId)
+ {
+ return PackageManager.Uninstall(applicationId);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Tizen.Applications;
+using System.Collections.Generic;
+using Homescreen.Debug;
+using Homescreen.Model;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// Manage the instance of RemoteView to prevent it
+ /// from being regenerated every time the Parent of Remoteview changes.
+ /// </summary>
+ public class RemoteViewStorage : IRemoteViewStorage
+ {
+ private static IDictionary<long, ReusableRemoteView> WidgetStorage = new Dictionary<long, ReusableRemoteView>();
+
+ /// <summary>
+ /// Returns the instance of the widget.
+ /// The instance may already be stored, or it may be a newly created instance.
+ /// </summary>
+ /// <param name="id">A unique ID that identifies the widget.</param>
+ /// <param name="widgetId">A widget ID that identifies the widget in the Tizen platform.</param>
+ /// <param name="content">A widget content</param>
+ /// <param name="period">A widget update period.</param>
+ /// <param name="previewImage">A widget preview image.</param>
+ /// <param name="overlayText">A text message displaying on the widget.</param>
+ /// <param name="loadingMessage">A text message during the widget is initializing.</param>
+ /// <returns>A instance of the widget</returns>
+ public static ReusableRemoteView Get(long id, string widgetId, string content,
+ double period, bool previewImage = true,
+ bool overlayText = true, bool loadingMessage = true)
+ {
+ if (WidgetStorage.TryGetValue(id, out ReusableRemoteView widget))
+ {
+ return widget;
+ }
+
+ var remoteView = new ReusableRemoteView(widgetId, content, period, previewImage, overlayText, loadingMessage);
+ WidgetStorage.Add(id, remoteView);
+ return remoteView;
+ }
+
+ /// <summary>
+ /// Initializes RemoteViewFactory.
+ /// </summary>
+ /// <param name="win">A base window for widgets</param>
+ public static void Init(EvasObject win)
+ {
+ RemoteViewFactory.Init(win);
+ }
+
+ /// <summary>
+ /// Finalizes the RemoteViewFactory.
+ /// </summary>
+ public static void Shutdown()
+ {
+ RemoteViewFactory.Shutdown();
+ }
+
+ private static void DeleteRemoteView(long id)
+ {
+ try
+ {
+ if (WidgetStorage.TryGetValue(id, out ReusableRemoteView widget))
+ {
+ widget.Layout.Hide();
+ WidgetStorage.Remove(id);
+ }
+ }
+ catch (Exception e)
+ {
+ TizenLog.Error($"{e.Message}");
+ }
+ }
+
+ /// <summary>
+ /// Delete the instance of the stored widget.
+ /// </summary>
+ /// <param name="widgetID">A widget ID.</param>
+ public void Delete(long widgetID)
+ {
+ DeleteRemoteView(widgetID);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Tizen.Applications;
+using Homescreen.View;
+using System;
+
+namespace Homescreen.Tizen.Mobile
+{
+ public class ReusableRemoteView
+ {
+ public Container Layout => box;
+ public IMouseEventReceiver Receiver
+ {
+ get => receiver;
+ set
+ {
+ receiver = value;
+ mouseEvent.Receiver = receiver;
+ }
+ }
+
+ public bool RepeatEvents
+ {
+ get => touchLayer.RepeatEvents;
+ set => touchLayer.RepeatEvents = value;
+ }
+
+ private RemoteView remoteView;
+ private IMouseEventReceiver receiver;
+ private MouseEvent mouseEvent;
+
+ private Box box;
+ private Rectangle touchLayer;
+
+ public ReusableRemoteView(string widgetId, string content, double period, bool previewImage = true, bool overlayText = true, bool loadingMessage = true)
+ {
+ box = new Box(Program.Window);
+ remoteView = RemoteViewFactory.Create(box, widgetId, content, period, previewImage, overlayText, loadingMessage);
+
+ touchLayer = new Rectangle(box)
+ {
+ Color = Color.Transparent,
+ RepeatEvents = true
+ };
+ touchLayer.Show();
+
+ box.PackEnd(remoteView.Layout);
+ box.PackEnd(touchLayer);
+
+ box.SetLayoutCallback(() =>
+ {
+ remoteView.Layout.Geometry = box.Geometry;
+ touchLayer.Geometry = box.Geometry;
+ });
+
+ mouseEvent = new MouseEvent(touchLayer, receiver);
+ }
+
+ public void Dispose()
+ {
+ mouseEvent.Detach();
+ }
+
+ public void SendEvent(RemoteView.Event ev)
+ {
+ remoteView.SendEvent(ev);
+ }
+
+ public void Resize(int width, int height)
+ {
+ remoteView.Layout.Resize(width, height);
+ box.Resize(width, height);
+ }
+
+ public void Show()
+ {
+ remoteView.Layout.Show();
+ box.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+using SQLite;
+using Xamarin.Forms;
+using SQLitePCL;
+using Homescreen.Tizen.Mobile;
+using Homescreen.Model.Interface;
+
+[assembly: Dependency(typeof(SQLitePort))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// SQLitePort manages SQLite to provide storages which is using for saving Widgets and Apps
+ /// </summary>
+ public class SQLitePort : ISQLite
+ {
+ /// <summary>
+ /// Create table
+ /// </summary>
+ /// <typeparam name="T">A DB item type that will be used to make a table.</typeparam>
+ /// <param name="conn">A SQLite connection</param>
+ public void CreateTable<T>(SQLiteConnection conn)
+ {
+ conn.CreateTable<T>();
+ }
+
+ /// <summary>
+ /// Provides SQLite connection which is main interface for data basing.
+ /// </summary>
+ /// <returns>A connection of SQLite</returns>
+ public SQLiteConnection GetConnection()
+ {
+ return GetConnection("homescreen.db3");
+ }
+
+ private SQLiteConnection GetConnection(string dbFilename)
+ {
+ raw.SetProvider(new SQLite3Provider_sqlite3());
+ raw.FreezeProvider(true);
+
+ string documentsPath = global::Tizen.Applications.Application.Current.DirectoryInfo.Data;
+ var path = Path.Combine(documentsPath, dbFilename);
+ SQLiteConnection conn = new SQLiteConnection(path);
+
+ return conn;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.Collections.Generic;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// TizenDeviceInfo provides APIs regarding screen size getting, status bar managing.
+ /// </summary>
+ public class TizenDeviceInfo : IDeviceInfo
+ {
+ /// <summary>
+ /// A width of device screen size
+ /// </summary>
+ public int Width => Program.ScreenSize.Width;
+
+ /// <summary>
+ /// A height of device screen size
+ /// </summary>
+ public int Height => Program.ScreenSize.Height;
+
+ private IDictionary<ElmSharp.StatusBarMode, StatusBarMode> BarMode = new Dictionary<ElmSharp.StatusBarMode, StatusBarMode>()
+ {
+ { ElmSharp.StatusBarMode.Opaque, StatusBarMode.Opaque },
+ { ElmSharp.StatusBarMode.Translucent, StatusBarMode.Translucent },
+ { ElmSharp.StatusBarMode.Transparent, StatusBarMode.Transparent },
+ };
+
+ /// <summary>
+ /// Mode of status bar displaying
+ /// </summary>
+ /// <see cref="StatusBarMode"/>
+ public StatusBarMode StatusBarMode
+ {
+ get => BarMode[Program.Window.StatusBarMode];
+ set
+ {
+ foreach (var mode in BarMode)
+ {
+ if (mode.Value == value)
+ {
+ Program.Window.StatusBarMode = mode.Key;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Interface;
+using Tizen.Xamarin.Forms.Extension;
+
+namespace Homescreen.Tizen.Mobile.Ports
+{
+ /// <summary>
+ /// Display a toast pop-up by using Tizen Toast extension
+ /// </summary>
+ public class ToastPopup : IToastPopup
+ {
+ /// <summary>
+ /// Display a toast pop-up with a given text.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ public void Display(string text)
+ {
+ Toast.DisplayText(text);
+ }
+
+ /// <summary>
+ /// Display a toast pop-up with a give text during given duration.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ /// <param name="duration">A display time in second.</param>
+ public void Display(string text, int duration)
+ {
+ Toast.DisplayText(text, duration);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Tizen.System;
+using Homescreen.Model.Interface;
+using Homescreen.Tizen.Mobile;
+using Tizen.Applications;
+
+[assembly: Dependency(typeof(WallpaperInfo))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WallpaperInfo provides APIs regarding device's wallpaper
+ /// co-working the Tizen application framework.
+ /// </summary>
+ public class WallpaperInfo : IWallpaperChanged
+ {
+ event EventHandler WallpaperEventHandler;
+
+ private void WallpaperHomeScreenChanged(object sender, WallpaperHomeScreenChangedEventArgs e)
+ {
+ WallpaperEventHandler?.Invoke(this, null);
+ }
+
+ /// <summary>
+ /// Get wallpaper full path.
+ /// </summary>
+ /// <returns>a wallpaper path</returns>
+ public string GetWallpaperPath()
+ {
+ return SystemSettings.WallpaperHomeScreen;
+ }
+
+ /// <summary>
+ /// Register an event handler to receive wallpaper update events.
+ /// </summary>
+ /// <param name="handler">An event handler will receive wallpaper update events</param>
+ public void RegisterWallpaperChangedCallback(EventHandler handler)
+ {
+ WallpaperEventHandler += handler;
+
+ if (WallpaperEventHandler.GetInvocationList().Length == 1)
+ {
+ SystemSettings.WallpaperHomeScreenChanged += WallpaperHomeScreenChanged;
+ }
+ }
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler will be deregistered.</param>
+ public void DeregisterWallpaperChangedCallback(EventHandler handler)
+ {
+ WallpaperEventHandler -= handler;
+ if (WallpaperEventHandler == null || WallpaperEventHandler.GetInvocationList().Length == 0)
+ {
+ SystemSettings.WallpaperHomeScreenChanged -= WallpaperHomeScreenChanged;
+ }
+ }
+
+ /// <summary>
+ /// Launch wallpaper settings to replace with another one.
+ /// </summary>
+ public void LaunchWallpaperSetting()
+ {
+ AppControl handle = new AppControl
+ {
+ ApplicationId = "org.tizen.wallpaper-ui-service",
+ Operation = AppControlOperations.Main,
+ };
+ handle.ExtraData.Add("from", "Homescreen-efl");
+ handle.ExtraData.Add("popup_type", "selection_popup");
+ handle.ExtraData.Add("setas-type", "Homescreen");
+ AppControl.SendLaunchRequest(handle);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.Collections.Generic;
+using System;
+using Tizen.Applications;
+using Homescreen.Debug;
+using Homescreen.DataModel;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WidgetManager provides APIs regarding application's widgets
+ /// by co-working with the Tizen applications framework.
+ /// </summary>
+ public class WidgetManager : IWidgetManager
+ {
+ /// <summary>
+ /// A widget list property.
+ /// </summary>
+ public List<WidgetInformation> WidgetList => GetWidgetList();
+
+ private IDictionary<WidgetControl.Scale.SizeType, WidgetInformation.SizeType> SizeTypes = new Dictionary<WidgetControl.Scale.SizeType, WidgetInformation.SizeType>()
+ {
+ { WidgetControl.Scale.SizeType.Basic4x2, WidgetInformation.SizeType.SIZE_4x2 },
+ { WidgetControl.Scale.SizeType.Basic4x4, WidgetInformation.SizeType.SIZE_4x4 },
+ };
+
+ /// <summary>
+ /// Get widgets information matching widgetId
+ /// </summary>
+ /// <param name="widgetId">A widget ID</param>
+ /// <returns>A list of widgets information</returns>
+ public List<WidgetInformation> GetWidgetInfo(string widgetId)
+ {
+ List<WidgetInformation> list = new List<WidgetInformation>();
+
+ WidgetControl widgetControl = new WidgetControl(widgetId);
+ IEnumerable<WidgetControl.Scale> scales;
+ string widgetName;
+
+ try
+ {
+ scales = widgetControl.GetScales();
+ widgetName = widgetControl.GetName("");
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug(e.Message);
+ return list;
+ }
+
+ foreach (var item in scales)
+ {
+ if (SizeTypes.TryGetValue(item.Type, out WidgetInformation.SizeType type))
+ {
+ list.Add(new WidgetInformation()
+ {
+ PreviewImagePath = item.PreviewImagePath,
+ Name = widgetName,
+ Type = SizeTypes[item.Type],
+ });
+ }
+ }
+
+ return list;
+ }
+
+ private List<WidgetInformation> GetWidgetList()
+ {
+ List<WidgetInformation> list = new List<WidgetInformation>();
+
+ foreach (var package in PackageManager.GetPackages())
+ {
+ string[] widgetLsit = WidgetControl.GetWidgetIds(package.Id);
+ foreach (var widgetId in widgetLsit)
+ {
+ foreach (var widget in GetWidgetInfo(widgetId))
+ {
+ widget.PackageId = package.Id;
+ widget.WidgetId = widgetId;
+ list.Add(widget);
+ }
+ }
+ }
+
+ return list;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View;
+using System;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(ClickableImage), typeof(ClickableImageRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// ClickableImageRenderer is a custom renderer for the CliableImage is like image button.
+ /// This custom renderer handles image replacing by the occurring events.
+ /// </summary>
+ public class ClickableImageRenderer : ImageRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+
+
+ if (Control == null)
+ {
+ return;
+ }
+
+ ClickableImage image = args.NewElement as ClickableImage;
+ if (image == null)
+ {
+ return;
+ }
+
+ /* If you change the value of Control.Color directly here, the changed value will not be applied.
+ * You should use the timer until the problem is resolved. */
+ Xamarin.Forms.Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.NormalColor.R * 255), (int)(image.NormalColor.G * 255), (int)(image.NormalColor.B * 255));
+ }
+
+ return false;
+ });
+
+ image.MouseDown += (s, e) =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.PressedColor.R * 255), (int)(image.PressedColor.G * 255), (int)(image.PressedColor.B * 255));
+ }
+ };
+ image.MouseUp += (s, e) =>
+ {
+ if (image != null && Control != null)
+ {
+ Control.Color = new Color((int)(image.NormalColor.R * 255), (int)(image.NormalColor.G * 255), (int)(image.NormalColor.B * 255));
+ }
+ };
+ mouseEvent = new MouseEvent(Control, image);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(EntryView), typeof(EntryViewRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// EntryViewRenderer is a custom renderer of EntryView for the text typing.
+ /// </summary>
+ public class EntryViewRenderer : EntryRenderer
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Entry> args)
+ {
+ base.OnElementChanged(args);
+
+ if (Control != null)
+ {
+ Control.SetInputPanelReturnKeyType(InputPanelReturnKeyType.Done);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Tizen.Mobile;
+using Homescreen.View.Widgets;
+using System.Collections.Generic;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(FastScrollLayout), typeof(FastScrollLayoutRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// FastScrollLayoutRenderer is custom renderer of FastScrollLayout which displays index with a scrolling function for the list like GUI controls.
+ /// </summary>
+ public class FastScrollLayoutRenderer : LayoutRenderer
+ {
+ private Index index;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> arg)
+ {
+ base.OnElementChanged(arg);
+
+ if (Control == null || arg.NewElement == null)
+ {
+ return;
+ }
+
+ FastScrollLayout scrollLayout = arg.NewElement as FastScrollLayout;
+ if (scrollLayout == null)
+ {
+ return;
+ }
+
+ if (index == null)
+ {
+ index = new Index(Control)
+ {
+ IsHorizontal = false,
+ AutoHide = false,
+ OmitEnabled = true,
+ };
+ index.Show();
+
+ var indexList = new List<string>
+ {
+ "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X", "Y", "Z"
+ };
+
+ foreach (var indexString in indexList)
+ {
+ var indexItem = index.Append(indexString);
+ indexItem.Selected += ((s, evt) =>
+ {
+ if (Element is FastScrollLayout layout)
+ {
+ layout.IndexSelected.Invoke(this, new IndexSelectedArgs()
+ {
+ Key = indexString,
+ });
+ }
+ });
+ }
+
+ index.Update(0);
+ Control.PackEnd(index);
+ Control.SetLayoutCallback(() =>
+ {
+ index.RaiseTop();
+ index.Geometry = Control.Geometry;
+ });
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+using Homescreen.View;
+using Homescreen.Debug;
+using Homescreen.Tizen.Mobile;
+
+[assembly: ExportRenderer(typeof(HomeScrollView), typeof(HomeScrollViewRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// HomeScrollViewRenderer is a custom renderer which handles HomeScrollView.
+ /// </summary>
+ ///<see cref="HomeScrollView"/>
+ public class HomeScrollViewRenderer : ScrollViewRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+
+ if (Control is Scroller scroller)
+ {
+ scroller.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ scroller.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ scroller.HorizontalRelativePageSize = 1;
+ scroller.HorizontalPageScrollLimit = 1;
+
+ mouseEvent = new MouseEvent(Control, args.NewElement as HomeScrollView);
+ }
+
+ if (args.NewElement != null && args.NewElement is HomeScrollView newScrollView)
+ {
+ newScrollView.HomeScrollViewScrollLockEventHandler += HomeScrollViewScrollLock;
+ newScrollView.HomeScrollViewScrollUnLockEventHandler += HomeScrollViewScrollUnlock;
+ }
+
+ if (args.OldElement != null && args.OldElement is HomeScrollView oldScrollview)
+ {
+ oldScrollview.HomeScrollViewScrollLockEventHandler -= HomeScrollViewScrollLock;
+ oldScrollview.HomeScrollViewScrollUnLockEventHandler -= HomeScrollViewScrollUnlock;
+ }
+ }
+
+ private void HomeScrollViewScrollLock(object sender, EventArgs e)
+ {
+ TizenLog.Debug($"HomeScrollViewScrollLock called");
+ (Control as Scroller).ScrollBlock = ScrollBlock.Horizontal;
+ }
+
+ private void HomeScrollViewScrollUnlock(object sender, EventArgs e)
+ {
+ TizenLog.Debug($"HomeScrollViewScrollUnlock called");
+ (Control as Scroller).ScrollBlock = ScrollBlock.None;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 ElmSharp;
+using Homescreen.Debug;
+using Homescreen.View;
+using Xamarin.Forms;
+using Homescreen.View.Widgets;
+
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// LongPressedTimer is a timer which is designed for the long press timing check.
+ /// </summary>
+ public class LongPressedTimer
+ {
+ /// <summary>
+ /// A flag indicates whether LongPressTimer is running or not.
+ /// </summary>
+ public bool IsRunning { get; private set; }
+
+ /// <summary>
+ /// A delegate of long press timer.
+ /// </summary>
+ public delegate void CompleteCallback();
+
+ private CompleteCallback longPressedCallback;
+ private int runningTime;
+
+ private bool isUsed;
+
+ /// <summary>
+ /// A setter method for the long pressed timer which will be called if long press timer is expired.
+ /// </summary>
+ /// <param name="callback">A delegate for mouse events</param>
+ public LongPressedTimer(CompleteCallback callback)
+ {
+ longPressedCallback = callback;
+ }
+
+ /// <summary>
+ /// Stop this timer.
+ /// </summary>
+ public void Stop()
+ {
+ IsRunning = false;
+ }
+
+ /// <summary>
+ /// Start a long press timer.
+ /// </summary>
+ public void Start()
+ {
+ if (isUsed)
+ {
+ return;
+ }
+
+ IsRunning = true;
+ isUsed = true;
+
+ runningTime = 0;
+ Device.StartTimer(TimeSpan.FromMilliseconds(5), () =>
+ {
+ runningTime += 5;
+ if (IsRunning == false)
+ {
+ return false;
+ }
+
+ if (runningTime >= 700)
+ {
+ IsRunning = false;
+ longPressedCallback();
+ return false;
+ }
+
+ return true;
+ });
+ }
+ }
+
+ /// <summary>
+ /// MouseEvent is a helper class that makes a Xamarin.Forms GUI element can handle mouse down, up, move events of a Tizen specific GUI control.
+ /// </summary>
+ public class MouseEvent
+ {
+ public IMouseEventReceiver Receiver { get; set; }
+
+ private EvasObjectEvent mouseDown;
+ private EvasObjectEvent mouseUp;
+ private EvasObjectEvent mouseMove;
+
+ private bool IsPressed;
+ private Xamarin.Forms.Point Down;
+ private Xamarin.Forms.Point Current;
+
+ private LongPressedTimer longTimer;
+
+ private static VisualElement topObject = null;
+
+ private bool IsChild(VisualElement parent, VisualElement child)
+ {
+ if (child == null)
+ {
+ return false;
+ }
+
+ var p = child.Parent;
+ while (p != null)
+ {
+ if (p == parent)
+ {
+ return true;
+ }
+
+ p = p.Parent as VisualElement;
+ }
+
+ return false;
+ }
+
+ public MouseEvent(EvasObject sender, IMouseEventReceiver receiver)
+ {
+ Receiver = receiver;
+
+ mouseDown = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseDown);
+ mouseDown.On += (s, e) =>
+ {
+
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (topObject == null)
+ {
+ topObject = Receiver as VisualElement;
+ }
+ else
+ {
+ if (IsChild(topObject, Receiver as VisualElement))
+ {
+ topObject = Receiver as VisualElement;
+ }
+ }
+
+ Device.StartTimer(TimeSpan.FromMilliseconds(10), () =>
+ {
+ if (topObject != (Receiver as VisualElement))
+ {
+ return false;
+ }
+
+ IsPressed = true;
+
+ Down = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+
+ Receiver.MouseDown?.Invoke(Receiver, new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ });
+
+ longTimer?.Stop();
+ longTimer = new LongPressedTimer(() =>
+ {
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseHold?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+ });
+ longTimer.Start();
+
+ return false;
+ });
+ };
+
+ mouseMove = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseMove);
+ mouseMove.On += (s, e) =>
+ {
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (IsPressed == false)
+ {
+ return;
+ }
+
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseMove?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+
+ if (longTimer.IsRunning && (Math.Pow(Current.X - Down.X, 2) + Math.Pow(Current.Y - Down.Y, 2) > 100))
+ {
+ longTimer.Stop();
+ }
+ };
+
+ mouseUp = new EvasObjectEvent(sender, EvasObjectCallbackType.MouseUp);
+ mouseUp.On += (s, e) =>
+ {
+ if (Receiver == null)
+ {
+ return;
+ }
+
+ if (IsPressed == false)
+ {
+ return;
+ }
+
+ topObject = null;
+
+ IsPressed = false;
+ Current = new Xamarin.Forms.Point { X = sender.EvasCanvas.Pointer.X, Y = sender.EvasCanvas.Pointer.Y };
+ Receiver.MouseUp?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+
+
+ if (longTimer.IsRunning)
+ {
+ longTimer.Stop();
+
+ TizenLog.Debug($"MouseEvent : Send Click {Receiver?.MouseUp?.GetType()}");
+ Receiver.MouseClick?.Invoke(Receiver, (new MouseEventArgs
+ {
+ Down = Down,
+ Current = Current,
+ }));
+ }
+ };
+
+ }
+
+ /// <summary>
+ /// remove all registered callback for the Tizen specific GUI control.
+ /// </summary>
+ public void Detach()
+ {
+ longTimer?.Stop();
+
+ mouseDown?.Dispose();
+ mouseUp?.Dispose();
+ mouseMove?.Dispose();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Tizen.Mobile.Renderer;
+using Xamarin.Forms.Platform.Tizen;
+using Homescreen.View.Widgets;
+using Homescreen.View;
+
+[assembly: ExportRenderer(typeof(AllpageThumbnailLayout), typeof(MouseEventRenderer))]
+namespace Homescreen.Tizen.Mobile.Renderer
+{
+ /// <summary>
+ /// MouseEventRenderer is a custom renderer for mouse events.
+ /// The MouseEventRenderer provides controls to the MouseEvent for mouse event handling after element creating.
+ /// </summary>
+ /// <see cref="MouseEvent"/>
+ public class MouseEventRenderer : LayoutRenderer
+ {
+ private MouseEvent mouseEvent;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> args)
+ {
+ base.OnElementChanged(args);
+
+ mouseEvent?.Detach();
+ if (args.NewElement is IMouseEventReceiver receiver)
+ {
+ mouseEvent = new MouseEvent(Control, receiver);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ mouseEvent?.Detach();
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Tizen.Mobile.Renderer;
+using Homescreen.View;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(NinePatch), typeof(NinePatchRenderer))]
+namespace Homescreen.Tizen.Mobile.Renderer
+{
+ /// <summary>
+ /// A custom renderer for NinePatchImage
+ /// </summary>
+ /// <see cref="NinePatch"/>
+ class NinePatchRenderer : ImageRenderer
+ {
+ /// <summary>
+ /// Updates border when Element is changed
+ /// </summary>
+ /// <param name="args">An image element changed event's argument </param>
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+ {
+ base.OnElementChanged(args);
+ UpdateBorder();
+ }
+
+ /// <summary>
+ /// Updates border when ElementProperty is changed
+ /// </summary>
+ /// <param name="sender">The source of the event</param>
+ /// <param name="args">An image element property changed event's argument </param>
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ if ((args.PropertyName == NinePatch.BorderBottomProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderLeftProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderRightProperty.PropertyName)
+ || (args.PropertyName == NinePatch.BorderTopProperty.PropertyName))
+ {
+ UpdateBorder();
+ }
+
+
+ base.OnElementPropertyChanged(sender, args);
+ }
+
+ /// <summary>
+ /// A method updates border of Control(Native Image)
+ /// </summary>
+ void UpdateBorder()
+ {
+ var img = Element as NinePatch;
+ Control?.SetBorder(img.BorderLeft, img.BorderRight, img.BorderTop, img.BorderBottom);
+ }
+
+ /// <summary>
+ /// A method updates border of Control(Native Image) after loading
+ /// </summary>
+ protected override void UpdateAfterLoading()
+ {
+ base.UpdateAfterLoading();
+ UpdateBorder();
+
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms.Platform.Tizen;
+using Tizen.Applications;
+using Homescreen.View.Widgets;
+using Homescreen.Tizen.Mobile;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.DataModel;
+
+[assembly: ExportRenderer(typeof(WidgetLayout), typeof(WidgetLayoutRenderer))]
+namespace Homescreen.Tizen.Mobile
+{
+ /// <summary>
+ /// WidgetLayoutRenderer is a custom renderer for the WidgetLayout.
+ /// The WidgetLayout displays an app's widget in the view.
+ /// </summary>
+ public class WidgetLayoutRenderer : LayoutRenderer
+ {
+ private ReusableRemoteView remoteView;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Layout> args)
+ {
+ base.OnElementChanged(args);
+
+ if (Control == null || args.NewElement == null)
+ {
+ return;
+ }
+
+ if (args.OldElement is WidgetLayout oldLayout)
+ {
+ oldLayout.WidgetSendCancelEventHandler = null;
+ oldLayout.WidgetTouchBlockEventHandler = null;
+ oldLayout.WidgetTouchUnlockEventHandler = null;
+ }
+
+ WidgetLayout widgetLayout = args.NewElement as WidgetLayout;
+ if (widgetLayout == null)
+ {
+ return;
+ }
+
+ try
+ {
+ remoteView = RemoteViewStorage.Get(
+ widgetLayout.WidgetInfo.Id, widgetLayout.WidgetInfo.WidgetId, widgetLayout.WidgetInfo.WidgetId, 0.0, true, false, false);
+
+ using (WidgetControl widgetControl = new WidgetControl(widgetLayout.WidgetInfo.WidgetId))
+ {
+ IEnumerable<WidgetControl.Scale> widgetScales = widgetControl.GetScales();
+ foreach (var scale in widgetScales)
+ {
+ if (widgetLayout.WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x4 && scale.Type == WidgetControl.Scale.SizeType.Basic4x4)
+ {
+ widgetLayout.WidthRequest = scale.Width;
+ widgetLayout.HeightRequest = scale.Height;
+ remoteView.Resize(scale.Width, scale.Height);
+
+ break;
+ }
+
+ if (widgetLayout.WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 && scale.Type == WidgetControl.Scale.SizeType.Basic4x2)
+ {
+ widgetLayout.WidthRequest = scale.Width;
+ widgetLayout.HeightRequest = scale.Height;
+ remoteView.Resize(scale.Width, scale.Height);
+ break;
+ }
+ }
+ }
+
+ remoteView.Show();
+ Control.PackEnd(remoteView.Layout);
+
+ Control.SetLayoutCallback(() =>
+ {
+ remoteView.Layout.Geometry = Control.Geometry;
+ });
+
+ widgetLayout.WidgetSendCancelEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Cancel ");
+ try
+ {
+ remoteView?.SendEvent(RemoteView.Event.CancelClick);
+ }
+ catch (Exception exception)
+ {
+ TizenLog.Error($"Failed to sendEvent(RemoteView.Event.CancelClick): {exception.Message}");
+ }
+ };
+
+ remoteView.Receiver = widgetLayout;
+
+ widgetLayout.WidgetTouchBlockEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Block ");
+ remoteView.RepeatEvents = false;
+ };
+
+ widgetLayout.WidgetTouchUnlockEventHandler = (s, e) =>
+ {
+ TizenLog.Debug($"widgetRenderer : Unlock ");
+ remoteView.RepeatEvents = true;
+ };
+ }
+ catch (Exception e)
+ {
+ TizenLog.Debug("Failed to create RemoteView : " + e.Message);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Control?.UnPack(remoteView.Layout);
+
+ base.Dispose(disposing);
+ }
+ }
+}
--- /dev/null
+<StyleCopSettings Version="105">
+ <GlobalSettings>
+ <StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
+ </GlobalSettings>
+ <Analyzers>
+ <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EnumerationItemsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustContainValidXml">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VoidReturnValueMustNotBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustMatchAccessors">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustNotBeEmpty">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustContainWhitespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustMeetCharacterPercentage">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationHeadersMustNotContainBlankLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludedDocumentationXPathDoesNotExist">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InheritDocMustBeUsedWithInheritingClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustBeSpelledCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMustHaveHeader">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustShowCopyright">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveCopyrightText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustContainFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveValidCompanyText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
+ <Rules>
+ <Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotContainUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotUseHungarianNotation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VariableNamesMustNotBePrefixed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotBeginWithUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeadersMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleBlankLinesInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ChainedStatementBlocksMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="WhileDoFooterMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeSeparatedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtStartOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtEndOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules">
+ <Rules>
+ <Rule Name="AccessModifierMustBeDeclared">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldsMustBePrivate">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeAnalysisSuppressionMustHaveJustification">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugAssertMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugFailMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ArithmeticExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConditionalExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveDelegateParenthesisWhenPossible">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AttributeConstructorMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveUnnecessaryCode">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
+ <Rules>
+ <Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustAppearInTheCorrectOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeOrderedByAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstantsMustAppearBeforeFields">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticElementsMustAppearBeforeInstanceElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DeclarationKeywordsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ProtectedMustComeBeforeInternal">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertyAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EventAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyElementsMustAppearBeforeStaticNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InstanceReadonlyElementsMustAppearBeforeInstanceNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NoValueFirstComparison">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SystemUsingDirectivesMustBePlacedBeforeOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingDirectivesMustBeOrderedAlphabeticallyByNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingStaticDirectivesMustBePlacedAfterUsingNamespaceDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="CommentsMustContainText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixLocalCallsWithThis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixCallsCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeOnDeclarationLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommaMustBeOnSameLineAsPreviousParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterListMustFollowDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustFollowComma">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustNotSpanMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustFollowPreviousClause">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPlaceRegionsWithinElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainEmptyStatements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedComments">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedRegions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseStringEmptyForEmptyStrings">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseBuiltInTypeAlias">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseShorthandForNullableTypes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.SpacingRules">
+ <Rules>
+ <Rule Name="CommasMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SemicolonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationLinesMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PreprocessorKeywordsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OperatorKeywordMustBeFollowedBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NullableTypeSymbolsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="MemberAccessSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncrementDecrementSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NegativeSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PositiveSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ColonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="TabsMustNotBeUsed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotSplitNullConditionalOperators">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.tizen.example.Homescreen.Tizen.Mobile" version="1.0.0" api-version="4" xmlns="http://tizen.org/ns/packages">
+ <profile name="mobile" />
+ <ui-application appid="org.tizen.example.Homescreen.Tizen.Mobile" exec="Homescreen.Tizen.Mobile.dll" multiple="false" nodisplay="false" taskmanage="true" splash-screen-display="true" type="dotnet" launch_mode="single">
+ <label>Homescreen</label>
+ <icon>Homescreen.Tizen.Mobile.png</icon>
+ <category name="http://tizen.org/category/homeapp"/>
+ <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+ </ui-application>
+ <shortcut-list />
+ <privileges>
+ <privilege>http://tizen.org/privilege/widget.viewer</privilege>
+ <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
+ <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
+ <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+ <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ <privilege>http://tizen.org/privilege/notification</privilege>
+ </privileges>
+ <provides-appdefined-privileges />
+</manifest>
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel
+{
+ /// <summary>
+ /// AppsInformation inherits ItemInformation and
+ /// is specified for the app.
+ /// </summary>
+ /// <see cref="ItemInformation"/>
+ public class AppInformation : ItemInformation
+ {
+ /// <summary>
+ /// A package ID
+ /// </summary>
+ public string PackageId { get; set; }
+
+ /// <summary>
+ /// An app ID
+ /// This information is different with the package ID by an app's packaging.
+ /// Actually the app ID is an unique identity information among apps.
+ /// </summary>
+ public string AppId { get; set; }
+
+ /// <summary>
+ /// A parent(folder) ID
+ /// </summary>
+ public long ParentId { get; set; } = ItemInformationDao.NoParent;
+
+ /// <summary>
+ /// A flag indicated whether an app is located in the SD card or not.
+ /// </summary>
+ public bool IsInSdCard { get; set; }
+
+ /// <summary>
+ /// A default constructor of AppInformation.
+ /// </summary>
+ public AppInformation()
+ {
+ }
+
+ /// <summary>
+ /// A constructor to copy from exist AppsInformation
+ /// </summary>
+ /// <param name="item">An existing app's information</param>
+ /// <see cref="ItemInformationDao"/>
+ public AppInformation(ItemInformationDao item)
+ {
+ Id = item.Id;
+ Label = string.IsNullOrWhiteSpace(item.Label) ? string.Empty : item.Label;
+ IconPath = item.IconPath;
+
+ PackageId = item.PackageId;
+ AppId = item.AppId;
+ ParentId = item.ParentId;
+ }
+
+ /// <summary>
+ /// A method provides ItemInformationDao object for DB storing.
+ /// </summary>
+ /// <returns>An ItemInformationDao will be stored in DB.</returns>
+ /// <see cref="ItemInformationDao"/>
+ public override ItemInformationDao GetDataObject()
+ {
+ return new ItemInformationDao
+ {
+ Id = Id,
+ Label = string.IsNullOrWhiteSpace(Label) ? string.Empty : Label,
+ IconPath = IconPath,
+ PackageId = PackageId,
+ AppId = AppId,
+ ParentId = ParentId,
+ };
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+
+namespace Homescreen.DataModel
+{
+ /// <summary>
+ /// a badge information
+ /// </summary>
+ public class BadgeInformation
+ {
+ /// <summary>
+ /// An app ID
+ /// </summary>
+ public String AppId
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// A number of badge notifications
+ /// </summary>
+ public int Count
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// A flag indicates whether a badge is displaying or not.
+ /// </summary>
+ public bool IsVisible
+ {
+ get; set;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 SQLite;
+
+namespace Homescreen.DataModel
+{
+ /// <summary>
+ /// A base class for all DB objects.
+ /// </summary>
+ public abstract class DataStorageObject
+ {
+ /// <summary>
+ /// A DB object id number.
+ /// This is incremental number using by DB system.
+ /// </summary>
+ [PrimaryKey, AutoIncrement, Column("_id")]
+ public long Id { get; set; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel
+{
+ /// <summary>
+ /// FolderInformation inherits ItemInformation and
+ /// is specified for the folder.
+ /// </summary>
+ /// <see cref="ItemInformation"/>
+ public class FolderInformation : ItemInformation
+ {
+ /// <summary>
+ /// A default icon path.
+ /// </summary>
+ public static readonly string FolderIconPath = "folder_appicon_bg.png";
+
+ /// <summary>
+ /// A Selected icon path.
+ /// </summary>
+ public static readonly string FolderSelectIconPath = "folder_appicon_bg_possible.png";
+
+ private List<AppInformation> appList = new List<AppInformation>();
+ /// <summary>
+ /// A containing app list of a folder.
+ /// </summary>
+ /// <see cref="AppInformation"/>
+ public IEnumerable<AppInformation> AppList
+ {
+ get => appList;
+ }
+
+ /// <summary>
+ /// A number of apps in a folder.
+ /// </summary>
+ public int AppCount
+ {
+ get
+ {
+ return appList.Count;
+ }
+ }
+
+ /// <summary>
+ /// A flag indicated whether this folder should be updated or not.
+ /// </summary>
+ public bool HaveToUpdate { get; set; }
+
+ /// <summary>
+ /// A constructor of the FolderInformation
+ /// </summary>
+ public FolderInformation()
+ {
+ HaveToUpdate = false;
+ }
+
+ /// <summary>
+ /// A constructor to copy folder information from exist one.
+ /// </summary>
+ /// <param name="item">A folder information to be copied.</param>
+ /// <see cref="ItemInformationDao"/>
+ public FolderInformation(ItemInformationDao item)
+ {
+ Id = item.Id;
+ Label = string.IsNullOrWhiteSpace(item.Label) ? String.Empty : item.Label;
+ IconPath = string.IsNullOrWhiteSpace(item.IconPath) ? FolderIconPath : item.IconPath;
+ }
+
+ /// <summary>
+ /// A method adds app into the folder.
+ /// </summary>
+ /// <param name="app">An app information to be added.</param>
+ /// <see cref="AppInformation"/>
+ public void AddApp(AppInformation app)
+ {
+ app.ParentId = Id;
+ appList.Add(app);
+ BadgeCount += app.BadgeCount;
+
+ appList.Sort((AppInformation a, AppInformation b) =>
+ {
+ return a.Label.CompareTo(b.Label);
+ });
+ }
+
+ /// <summary>
+ /// A method removes app from the folder.
+ /// </summary>
+ /// <param name="Id">An id of an app to be removed.</param>
+ public void RemoveApp(long Id)
+ {
+ foreach (var app in AppList)
+ {
+ if (app.Id == Id)
+ {
+ BadgeCount -= app.BadgeCount;
+ appList.Remove(app);
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method update folder badge count forcely.
+ /// </summary>
+ public void UpdateBadgeCount()
+ {
+ BadgeCount = 0;
+ foreach (var app in AppList)
+ {
+ BadgeCount += app.BadgeCount;
+ }
+ }
+
+ /// <summary>
+ /// A method clear all apps from the folder.
+ /// </summary>
+ public void ClearAllApps()
+ {
+ BadgeCount = 0;
+ appList.Clear();
+ }
+
+ /// <summary>
+ /// A method provides ItemInformationDao object for DB storing.
+ /// </summary>
+ /// <returns>An ItemInformationDao will be stored in DB.</returns>
+ /// <see cref="ItemInformationDao"/>
+ public override ItemInformationDao GetDataObject()
+ {
+ return new ItemInformationDao()
+ {
+ Id = Id,
+ Label = Label,
+ IconPath = IconPath,
+ IsFolder = true,
+ };
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel
+{
+ /// <summary>
+ /// An install app information strong class(data model)
+ /// </summary>
+ public class InstalledAppInformation
+ {
+ /// <summary>
+ /// A package ID of the installed app.
+ /// </summary>
+ public string PackageId
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// An app ID of the installed app.
+ /// </summary>
+ public string AppId
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// An app's display name
+ /// </summary>
+ public string Title
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// An app icon full path
+ /// </summary>
+ public string IconPath
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// A flag indicates whether an app is located in SD card or not.
+ /// </summary>
+ public bool IsInSdCard
+ {
+ get; set;
+
+ }
+
+ /// <summary>
+ /// A flag indicates whether an app is removable or not.
+ /// Actually the preload app cannot be removed.
+ /// </summary>
+ public bool IsRemovable
+ {
+ get; set;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.DataModel
+{
+ /// <summary>
+ /// An ItemInforation is base class of AppsInformation and FolderInformation.
+ /// </summary>
+ /// <see cref="AppInformation"/>
+ /// <see cref="FolderInformation"/>
+ public abstract class ItemInformation : BindableObject, IComparable<ItemInformation>
+ {
+ /// <summary>
+ /// A default ID for the item.
+ /// </summary>
+ public static readonly long InitialId = -1;
+
+ /// <summary>
+ /// An item ID
+ /// </summary>
+ public long Id { get; set; } = InitialId;
+
+ /// <summary>
+ /// An item label
+ /// </summary>
+ public string Label { get; set; }
+
+ /// <summary>
+ /// An icon file full path
+ /// </summary>
+ public string IconPath { get; set; }
+
+ /// <summary>
+ /// A number of badge notifications
+ /// </summary>
+ public int BadgeCount { get; set; }
+
+ /// <summary>
+ /// A flag indicates whether this item is removable or not.
+ /// </summary>
+ public bool IsRemovable { get; set; }
+
+ /// <summary>
+ /// A flag indicates whether this item is selected or not.
+ /// </summary>
+ public bool IsChecked { get; set; }
+
+ /// <summary>
+ /// An index number
+ /// </summary>
+ public int Index { get; set; }
+
+ /// <summary>
+ /// A method provides ItemInformationDao object for DB storing.
+ /// </summary>
+ /// <returns>An ItemInformationDao will be stored in DB.</returns>
+ /// <see cref="ItemInformationDao"/>
+ public abstract ItemInformationDao GetDataObject();
+
+ /// <summary>
+ /// A compare function for sorting or matching
+ /// </summary>
+ /// <param name="other">Other information to be compared</param>
+ /// <returns>A compare result, 1 will be returned if both items are same.</returns>
+ public int CompareTo(ItemInformation other)
+ {
+ return Id.CompareTo(other.Id);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel
+{
+ /// <summary>
+ /// An ItemInformationDao is used for the DB inserting or retrieving.
+ /// </summary>
+ public class ItemInformationDao : DataStorageObject
+ {
+ /// <summary>
+ /// A default ID
+ /// </summary>
+ public static readonly int NoParent = -1;
+
+ /// <summary>
+ /// A label
+ /// </summary>
+ public string Label { get; set; }
+
+ /// <summary>
+ /// An icon full path
+ /// </summary>
+ public string IconPath { get; set; }
+
+ /// <summary>
+ /// A package ID
+ /// </summary>
+ /// <remarks>This value will be empty if this is a folder.</remarks>
+ public string PackageId { get; set; }
+
+ /// <summary>
+ /// An app ID
+ /// </summary>
+ /// <remarks>This value will be empty if this is a folder.</remarks>
+ public string AppId { get; set; }
+
+ /// <summary>
+ /// A parent ID
+ /// </summary>
+ /// <remarks>This value will be empty if this is a folder.</remarks>
+ public long ParentId { get; set; }
+
+ /// <summary>
+ /// A flag indicates whether this item is a folder(true) or an app(false).
+ /// </summary>
+ public bool IsFolder { get; set; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+
+namespace Homescreen.DataModel
+{
+ /// <summary>
+ /// An information class for a widget.
+ /// </summary>
+ public class WidgetInformation : DataStorageObject
+ {
+ /// <summary>
+ /// An enumeration of widget sizes.
+ /// </summary>
+ public enum SizeType
+ {
+ /// <summary>
+ /// A widget size. (width = 4, height = 2)
+ /// </summary>
+ SIZE_4x2 = 8,
+
+ /// <summary>
+ /// A widget size. (width = 4, height = 4)
+ /// </summary>
+ SIZE_4x4 = 16,
+ }
+
+ /// <summary>
+ /// A package ID of widget providing app.
+ /// </summary>
+ public string PackageId { get; set; }
+
+ /// <summary>
+ /// A widget ID
+ /// </summary>
+ public string WidgetId { get; set; }
+
+ /// <summary>
+ /// A widget name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// A widget size.
+ /// </summary>
+ public SizeType Type { get; set; }
+
+ private string previewPath;
+
+ /// <summary>
+ /// A preview image of widget.
+ /// </summary>
+ public string PreviewImagePath
+ {
+ get
+ {
+ if (String.IsNullOrEmpty(previewPath))
+ {
+ List<WidgetInformation> widgetinfo = DependencyService.Get<IWidgetManager>()?.GetWidgetInfo(WidgetId);
+ foreach (var info in widgetinfo)
+ {
+ if (Type == info.Type)
+ {
+ previewPath = info.PreviewImagePath;
+ }
+ }
+ }
+
+ return previewPath;
+ }
+
+ set => previewPath = value;
+ }
+
+ /// <summary>
+ /// A page index of widget displaying.
+ /// </summary>
+ public int PageIndex { get; set; }
+
+ /// <summary>
+ /// A widget x position in a page.
+ /// </summary>
+ public int X { get; set; }
+
+ /// <summary>
+ /// A widget y position in a page.
+ /// </summary>
+ public int Y { get; set; }
+ }
+
+ /// <summary>
+ /// A widget helper class which is using for widget add scenario.
+ /// </summary>
+ public class WidgetInformationToAdd
+ {
+ /// <summary>
+ /// A widget information.
+ /// </summary>
+ public WidgetInformation Info { get; private set; }
+ /// <summary>
+ /// An event handler which will be called if adding is failed.
+ /// </summary>
+ public event EventHandler FailedToAdd;
+
+ /// <summary>
+ /// A method will be called if widget adding is failed.
+ /// </summary>
+ public void ThrowAway() => FailedToAdd?.Invoke(this, EventArgs.Empty);
+
+ /// <summary>
+ /// A constructor of WidgetInformationToAdd.
+ /// </summary>
+ /// <param name="widgetInfo">A widget information will be added.</param>
+ public WidgetInformationToAdd(WidgetInformation widgetInfo) => Info = widgetInfo;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.ObjectModel;
+using System;
+
+namespace Homescreen.DataModel
+{
+ /// <summary>
+ /// WidgetPageInformation which is a data model of the HomeScreen WidgetPageLayout.
+ /// </summary>
+ public class WidgetPageInformation
+ {
+ private int index;
+
+ /// <summary>
+ /// A page index of a widget page.
+ /// </summary>
+ public int PageIndex
+ {
+ get => index;
+ set
+ {
+ index = value;
+
+ if (Widgets != null)
+ {
+ foreach (var widget in Widgets)
+ {
+ widget.PageIndex = value;
+ }
+ }
+
+ PageIndexChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// A containing widget informations.
+ /// </summary>
+ public ObservableCollection<WidgetInformation> Widgets { get; set; }
+
+ public event EventHandler PageIndexChanged;
+ }
+
+ /// <summary>
+ /// WidgetPageCountInformation which has just a number of WidgetPages.
+ /// This class's instance data will be stored in the HomeScreen storage by HomeWidgetProvider.
+ /// </summary>
+ public class WidgetPageCountInformation : DataStorageObject
+ {
+ /// <summary>
+ /// A number of widget pages.
+ /// </summary>
+ public int PageCount { get; set; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Runtime.CompilerServices;
+
+namespace Homescreen.Debug
+{
+ /// <summary>
+ /// ExecutePrint prints logs when it created and deleted by scoping out.
+ /// This class can be instantiated to debug method's start and end time.
+ /// </summary>
+ public class ExecutePrint
+ {
+ private string functionName;
+
+ /// <summary>
+ /// A Constructor of ExecutePrint,
+ /// will print a log with file name, function name and line number.
+ /// </summary>
+ /// <param name="file">A file name</param>
+ /// <param name="func">A function name</param>
+ /// <param name="line">A line number</param>
+ public ExecutePrint([CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Log.Debug(func + " is Enter", file, func, line);
+ functionName = func;
+ }
+
+
+ /// <summary>
+ /// This destructor will print a log when this ExecutePrint instance is deleted.
+ /// But the deleting time is not be exact same with an instance scope depending on GC's behavior.
+ /// </summary>
+ ~ExecutePrint()
+ {
+ Log.Debug(functionName + " is Exit");
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug
+{
+ /// <summary>
+ /// An interface for logging.
+ /// </summary>
+ public interface ILog
+ {
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ void Debug(string message, string file, string func, int line);
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ void Debug(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ void Error(string message, string file, string func, int line);
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ void Error(string tag, string message, string file, string func, int line);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Runtime.CompilerServices;
+using Xamarin.Forms;
+
+namespace Homescreen.Debug
+{
+ /// <summary>
+ /// An utility class for logging.
+ /// </summary>
+ public class Log
+ {
+ /// <summary>
+ /// A method for printing a debug message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public static void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ var logger = DependencyService.Get<ILog>();
+ logger?.Debug(message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing a debug message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public static void Debug(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ var logger = DependencyService.Get<ILog>();
+ logger?.Debug(tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message.
+ /// </summary>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public static void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ var logger = DependencyService.Get<ILog>();
+ logger?.Error(message, file, func, line);
+ }
+
+ /// <summary>
+ /// A method for printing an error message with a tag.
+ /// </summary>
+ /// <param name="tag">A tag for logging category</param>
+ /// <param name="message">A debugging message</param>
+ /// <param name="file">A caller file name</param>
+ /// <param name="func">A caller function name</param>
+ /// <param name="line">A line number</param>
+ public static void Error(string tag, string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ var logger = DependencyService.Get<ILog>();
+ logger?.Error(tag, message, file, func, line);
+ }
+ }
+}
--- /dev/null
+using Homescreen.View;
+using Xamarin.Forms;
+using Homescreen.Debug;
+using System;
+
+namespace Homescreen
+{
+ public class App : Application
+ {
+ public HomescreenMainPage HomeMainPage { get; private set; }
+
+ public App()
+ {
+ Log.Debug("---Homescreen start---");
+
+ HomeMainPage = new HomescreenMainPage();
+ NavigationPage.SetHasBackButton(HomeMainPage, false);
+ NavigationPage.SetHasNavigationBar(HomeMainPage, false);
+
+ MainPage = new NavigationPage(HomeMainPage);
+ }
+
+ public void LaunchByHome()
+ {
+ int currentPageIndex = Current.MainPage.Navigation.NavigationStack.Count - 1;
+ if (Current.MainPage.Navigation.NavigationStack[currentPageIndex] is IBackHomeSignalReceiver page)
+ {
+ page?.OnHomeKeyPressed();
+ }
+ }
+
+ public void MenuKeyPressed()
+ {
+ int currentPageIndex = Current.MainPage.Navigation.NavigationStack.Count - 1;
+ if (Current.MainPage.Navigation.NavigationStack[currentPageIndex] is IBackHomeSignalReceiver page)
+ {
+ page?.OnMenuKeyPressed();
+ }
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+ <NoWarn>1701;1702;1705;NU1701</NoWarn>
+ </PropertyGroup>
+
+ <!-- Include Nuget Package for Xamarin building -->
+ <ItemGroup>
+ <PackageReference Include="Xamarin.Forms" Version="2.5.0.77107" />
+ <PackageReference Include="Plugin.SQLite" Version="1.0.4" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Update="View\Apps\AppsLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\AppsPageLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\AppsPageScrollView.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\BadgeLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\CheckBox.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\ChooserTopBar.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\FolderLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\ClickableImage.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\HomescreenMainPage.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Apps\ItemLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\NinePatch.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\OptionButton.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\IndicatorUnit.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\PageIndicator.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Wallpaper.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\AddWidgetPage.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\FastScrollLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\PreviewImage.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\WidgetHoldImage.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\WidgetLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\WidgetPageLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\WidgetPageScrollView.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Widgets\WidgetsLayout.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ <Compile Update="View\Wallpaper.xaml.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
+ </ItemGroup>
+</Project>
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Model.Interface;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Homescreen.Debug;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// AppsDataProvides includes APIs to item set, get, update, delete.
+ /// Actually this is working with HomescreenDataStorage to store items.
+ /// </summary>
+ /// <see cref="AppsDataProvider"/>
+ public class AppsDataProvider : IAppsDataProvider
+ {
+ private static readonly Lazy<AppsDataProvider> lazy
+ = new Lazy<AppsDataProvider>(() => new AppsDataProvider());
+
+ /// <summary>
+ /// An instance of AppsDataProvider.
+ /// </summary>
+ public static IAppsDataProvider Instance
+ {
+ get => lazy.Value;
+ }
+
+ private AppsDataProvider()
+ {
+ /*
+ string msg;
+ if (false == Test(out msg))
+ {
+ Log.Debug(msg);
+ }
+ */
+ }
+
+ /// <summary>
+ /// Add item information to the storage.
+ /// </summary>
+ /// <param name="item">An item information which is describing an app or a folder.</param>
+ /// <returns>An updated item ID</returns>
+ public long Set(ItemInformation item)
+ {
+ return HomescreenDataStorage.Instance.Insert<ItemInformationDao>(item.GetDataObject());
+ }
+
+ /// <summary>
+ /// Update item information.
+ /// </summary>
+ /// <param name="item">An item information contains updated information.</param>
+ /// <returns>An updated item ID</returns>
+ public long Update(ItemInformation item)
+ {
+ return HomescreenDataStorage.Instance.Update<ItemInformationDao>(item.GetDataObject());
+ }
+
+ /// <summary>
+ /// Add items information to the storage.
+ /// </summary>
+ /// <param name="items">An items information</param>
+ public void Set(IList<ItemInformation> items)
+ {
+ foreach (var item in items)
+ {
+ item.Id = Update(item);
+ if (item is FolderInformation folder)
+ {
+ foreach (var inApp in folder.AppList)
+ {
+ inApp.ParentId = item.Id;
+ inApp.Id = Update(inApp);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Provides stored items information.
+ /// </summary>
+ /// <returns>An items information list</returns>
+ public IList<ItemInformation> Get()
+ {
+ var daoItems = HomescreenDataStorage.Instance.Read<ItemInformationDao>();
+ var itemList = new List<ItemInformation>();
+ var folderDictionary = new Dictionary<long, FolderInformation>();
+
+ foreach (var daoItem in daoItems)
+ {
+ if (daoItem.IsFolder)
+ {
+ if (folderDictionary.ContainsKey(daoItem.Id))
+ {
+ folderDictionary[daoItem.Id].Id = daoItem.Id;
+ folderDictionary[daoItem.Id].Label = daoItem.Label ?? String.Empty;
+ folderDictionary[daoItem.Id].IconPath = string.IsNullOrWhiteSpace(daoItem.IconPath) ? FolderInformation.FolderIconPath : daoItem.IconPath;
+ }
+ else
+ {
+ folderDictionary.Add(daoItem.Id, new FolderInformation(daoItem));
+ }
+
+ }
+ else
+ {
+ if (daoItem.ParentId != ItemInformationDao.NoParent)
+ {
+ if (folderDictionary.ContainsKey(daoItem.ParentId))
+ {
+ folderDictionary[daoItem.ParentId].AddApp(new AppInformation(daoItem));
+ }
+ else
+ {
+ var emptyFolder = new FolderInformation()
+ {
+ Id = daoItem.ParentId,
+ };
+ emptyFolder.AddApp(new AppInformation(daoItem));
+ folderDictionary.Add(daoItem.ParentId, emptyFolder);
+ }
+ }
+ else
+ {
+ itemList.Add(new AppInformation(daoItem));
+ }
+
+ }
+ }
+
+ itemList.AddRange(folderDictionary.Values);
+ itemList.Sort((ItemInformation a, ItemInformation b) =>
+ {
+ if (a.Label == null)
+ {
+ return -1;
+ }
+
+ if (b.Label == null)
+ {
+ return 1;
+ }
+
+ return a.Label.CompareTo(b.Label);
+ });
+ return itemList;
+ }
+
+ /// <summary>
+ /// Remove an item from the storage
+ /// </summary>
+ /// <param name="item">An item information to be removed</param>
+ public void Remove(ItemInformation item)
+ {
+ HomescreenDataStorage.Instance.Delete<ItemInformationDao>(item.GetDataObject());
+ }
+
+ /// <summary>
+ /// Regression test method
+ /// </summary>
+ /// <param name="message">A result message</param>
+ /// <returns>A testing result. True if the test is succeed.</returns>
+ public bool Test(out string message)
+ {
+
+ try
+ {
+ var testAppList = new List<ItemInformation>
+ {
+ new AppInformation()
+ {
+ Label = "TTestApp1",
+ IconPath = "IconPath1",
+ BadgeCount = 2,
+ IsRemovable = true,
+
+ PackageId = "org.tizen.example.app1",
+ AppId = "org.tizen.example.app1",
+ IsInSdCard = false,
+ },
+
+ new AppInformation()
+ {
+ Label = "TTestApp2",
+ IconPath = "IconPath2",
+ BadgeCount = 3,
+ IsRemovable = true,
+
+ PackageId = "org.tizen.example.app2",
+ AppId = "org.tizen.example.app2",
+ IsInSdCard = false,
+ },
+
+ new AppInformation()
+ {
+ Label = "TTestApp3",
+ IconPath = "IconPath3",
+ BadgeCount = 4,
+ IsRemovable = true,
+
+ PackageId = "org.tizen.example.app3",
+ AppId = "org.tizen.example.app3",
+ IsInSdCard = false,
+ }
+ };
+
+ var folder = new FolderInformation()
+ {
+ Label = "Folder",
+ IconPath = "IconPath3",
+ BadgeCount = 4,
+ IsRemovable = true,
+ };
+
+ var folderAppList = new List<AppInformation>
+ {
+ new AppInformation()
+ {
+ Label = "TTestApp4",
+ IconPath = "IconPath4",
+ BadgeCount = 5,
+ IsRemovable = true,
+
+ PackageId = "org.tizen.example.app4",
+ AppId = "org.tizen.example.app4",
+ IsInSdCard = false,
+ },
+
+ new AppInformation()
+ {
+ Label = "TTestApp5",
+ IconPath = "IconPath5",
+ BadgeCount = 6,
+ IsRemovable = true,
+
+ PackageId = "org.tizen.example.app5",
+ AppId = "org.tizen.example.app5",
+ IsInSdCard = false,
+ }
+ };
+
+ Log.Debug("Due to AppsDataProvider testing, all stored app data will be removed!!!");
+
+ // Initialize Test
+ Log.Debug("[Initialize Test]");
+
+ var list = Get();
+ foreach (var item in list)
+ {
+ if (item is FolderInformation folderInfo)
+ {
+ foreach (var inApp in folderInfo.AppList)
+ {
+ Remove(inApp);
+ }
+ }
+
+ Remove(item);
+ }
+
+ list = Get();
+ if (list.Count() != 0)
+ {
+ message = "Test Initialization is failed, count should be 0, but = " + list.Count();
+ return false;
+ }
+
+ // Set App Test
+
+ Log.Debug("[Set App Test]");
+
+ testAppList[0].Id = Set(testAppList[0]);
+
+ list = Get();
+ if (list.Count() != 1)
+ {
+ message = "Set is failed, count should be 1, but = " + list.Count();
+ return false;
+ }
+
+ if (list.ElementAt(0) is AppInformation app1)
+ {
+ var originalApp = testAppList[0] as AppInformation;
+ if (app1.Label != originalApp.Label)
+ {
+ message = $"Set is failed, Label value is different, {app1.Label} - {originalApp.Label}";
+ return false;
+ }
+
+ if (app1.IconPath != originalApp.IconPath)
+ {
+ message = $"Set is failed, IconPath value is different, {app1.IconPath} - {originalApp.IconPath}";
+ return false;
+ }
+
+ if (app1.PackageId != originalApp.PackageId)
+ {
+ message = $"Set is failed, PackageId value is different, {app1.PackageId} - {originalApp.PackageId}";
+ return false;
+ }
+
+ if (app1.AppId != originalApp.AppId)
+ {
+ message = $"Set is failed, AppId value is different, {app1.AppId} - {originalApp.AppId}";
+ return false;
+ }
+ }
+ else
+ {
+ message = "Set is failed, type is wrong, type is " + list.ElementAt(0);
+ return false;
+ }
+
+ // Remove Test
+ Log.Debug("[App Remove Test]");
+ Remove(list.ElementAt(0));
+ list = Get();
+ if (list.Count() != 0)
+ {
+ message = "Remove is failed, count should be 0, but = " + list.Count();
+ return false;
+ }
+
+ // Set Folder Test
+ Log.Debug("[Set Folder Test]");
+ folder.Id = Set(folder);
+
+ list = Get();
+ if (list.Count() != 1)
+ {
+ message = "Set folder is failed, count should be 1, but = " + list.Count();
+ return false;
+ }
+
+ if (list.ElementAt(0) is FolderInformation folder1)
+ {
+ if (folder1.Label != folder.Label)
+ {
+ message = $"Set folder is failed, Label value is different, {folder1.Label} - {folder.Label}";
+ return false;
+ }
+
+ if (folder1.IconPath != folder.IconPath)
+ {
+ message = $"Set folder is failed, IconPath value is different, {folder1.IconPath} - {folder.IconPath}";
+ return false;
+ }
+
+ folder.AddApp(folderAppList[0]);
+ folder.AddApp(folderAppList[1]);
+ Update(folder);
+
+ foreach (var app in folder.AppList)
+ {
+ app.Id = Set(app);
+ }
+ }
+ else
+ {
+ message = "Set folder is failed, wrong type, but = " + list.ElementAt(0);
+ return false;
+ }
+
+ list = Get();
+ if (list.Count() != 1)
+ {
+ message = "Set folder is failed, count should be 1, but = " + list.Count();
+ return false;
+ }
+
+ if (list.ElementAt(0) is FolderInformation folder2)
+ {
+ if (folder2.Label != folder.Label)
+ {
+ message = $"Set folder is failed, Label value is different, {folder2.Label} - {folder.Label}";
+ return false;
+ }
+
+ if (folder2.IconPath != folder.IconPath)
+ {
+ message = $"Set folder is failed, IconPath value is different, {folder2.IconPath} - {folder.IconPath}";
+ return false;
+ }
+
+ if (folder2.AppList.Count() != 2)
+ {
+ message = $"Set folder is failed, app list count should be 2, but = " + folder2.AppList.Count();
+ return false;
+ }
+
+ int i = 0;
+ foreach (var item in folder2.AppList)
+ {
+ if (item is AppInformation inApp)
+ {
+ var originalApp = folderAppList[i];
+ if (inApp.Label != originalApp.Label)
+ {
+ message = $"Set folder is failed, Label value is different, {inApp.Label} - {originalApp.Label}";
+ return false;
+ }
+
+ if (inApp.IconPath != originalApp.IconPath)
+ {
+ message = $"Set folder is failed, IconPath value is different, {inApp.IconPath} - {originalApp.IconPath}";
+ return false;
+ }
+
+ if (inApp.PackageId != originalApp.PackageId)
+ {
+ message = $"Set folder is failed, PackageId value is different, {inApp.PackageId} - {originalApp.PackageId}";
+ return false;
+ }
+
+ if (inApp.AppId != originalApp.AppId)
+ {
+ message = $"Set folder is failed, AppId value is different, {inApp.AppId} - {originalApp.AppId}";
+ return false;
+ }
+ }
+ else
+ {
+ message = "Set folder is failed, inside app is wrong type, " + item;
+ return false;
+ }
+
+ i += 1;
+ }
+ }
+ else
+ {
+ message = "Set folder is failed, wrong type, but = " + list.ElementAt(0);
+ return false;
+ }
+
+
+ // Set(List) Test
+ Log.Debug("[Set List Test]");
+ testAppList.Add(folder);
+ Set(testAppList);
+
+ list = Get();
+ if (list.Count() != 4)
+ {
+ message = "Set(list) is failed, count should be 4, but = " + list.Count();
+ return false;
+ }
+
+ var index = 0;
+ foreach (var appItem in list)
+ {
+ if (appItem is AppInformation app2)
+ {
+ var originalApp = testAppList[index++] as AppInformation;
+ if (app2.Label != originalApp.Label)
+ {
+ message = $"Set(list) is failed, Label value is different, {app2.Label} - {originalApp.Label}";
+ return false;
+ }
+
+ if (app2.IconPath != originalApp.IconPath)
+ {
+ message = $"Set(list) is failed, IconPath value is different, {app2.IconPath} - {originalApp.IconPath}";
+ return false;
+ }
+
+ if (app2.PackageId != originalApp.PackageId)
+ {
+ message = $"Set(list) is failed, PackageId value is different, {app2.PackageId} - {originalApp.PackageId}";
+ return false;
+ }
+
+ if (app2.AppId != originalApp.AppId)
+ {
+ message = $"Set(list) is failed, AppId value is different, {app2.AppId} - {originalApp.AppId}";
+ return false;
+ }
+
+ if (app2.IsInSdCard != false)
+ {
+ message = $"Set(list) is failed, IsInSdCard value is different, {app2.IsInSdCard} - {originalApp.IsInSdCard}";
+ return false;
+ }
+ }
+ else if (appItem is FolderInformation folder3)
+ {
+ if (folder3.Label != folder.Label)
+ {
+ message = $"Set(list) folder is failed, Label value is different, {folder3.Label} - {folder.Label}";
+ return false;
+ }
+
+ if (folder3.IconPath != folder.IconPath)
+ {
+ message = $"Set(list) folder is failed, IconPath value is different, {folder3.IconPath} - {folder.IconPath}";
+ return false;
+ }
+
+ if (folder3.AppCount != 2)
+ {
+ message = $"Set(list) folder is failed, app list count should be 2, but = " + folder3.AppList.Count();
+ return false;
+ }
+
+ int i = 0;
+ foreach (var item in folder3.AppList)
+ {
+ if (item is AppInformation inApp)
+ {
+ var originalApp = folderAppList[i];
+ if (inApp.Label != originalApp.Label)
+ {
+ message = $"Set(list) folder is failed, Label value is different, {inApp.Label} - {originalApp.Label}";
+ return false;
+ }
+
+ if (inApp.IconPath != originalApp.IconPath)
+ {
+ message = $"Set(list) folder is failed, IconPath value is different, {inApp.IconPath} - {originalApp.IconPath}";
+ return false;
+ }
+
+ if (inApp.PackageId != originalApp.PackageId)
+ {
+ message = $"Set(list) folder is failed, PackageId value is different, {inApp.PackageId} - {originalApp.PackageId}";
+ return false;
+ }
+
+ if (inApp.AppId != originalApp.AppId)
+ {
+ message = $"Set(list) folder is failed, AppId value is different, {inApp.AppId} - {originalApp.AppId}";
+ return false;
+ }
+ }
+ else
+ {
+ message = "Set(list) folder is failed, inside app is wrong type, " + item;
+ return false;
+ }
+
+ i += 1;
+ }
+ }
+ else
+ {
+ message = "Set(list) is failed, type is wrong, type is " + appItem;
+ return false;
+ }
+ }
+
+ // Remove Test
+ Log.Debug("[List Remove Test]");
+ var firstItemLabel = list.ElementAt(0).Label;
+
+ if ((list.ElementAt(0) is FolderInformation) == false)
+ {
+ message = "Remove(list, first) is failed, first item should be folder item";
+ return false;
+ }
+
+ var firstItem = list.ElementAt(0) as FolderInformation;
+ foreach (var inApp in firstItem.AppList)
+ {
+ Remove(inApp);
+ }
+
+ Remove(firstItem);
+
+ list = Get();
+
+ foreach (var appItem in list)
+ {
+ if (appItem.Label == firstItemLabel)
+ {
+ message = "Remove(list, first) is failed, wrong item is removed.";
+ return false;
+ }
+ }
+
+ var lastItemLabel = list.Last().Label;
+ Remove(list.Last());
+
+ list = Get();
+ if (list.Count() != 2)
+ {
+ message = "Remove(list, last) is failed, count should be 2, but = " + list.Count();
+ return false;
+ }
+
+ foreach (var appItem in list)
+ {
+ if (appItem.Label == lastItemLabel)
+ {
+ message = "Remove(list, last) is failed, wrong item is removed.";
+ return false;
+ }
+
+ Remove(appItem);
+ }
+
+
+ list = Get();
+ if (list.Count() != 0)
+ {
+ message = "Remove(list) is failed, count should be 0, but = " + list.Count();
+ return false;
+ }
+
+ message = "*** AppsDataProvider Minimum Test Succeed!!! ***";
+ Log.Debug(message);
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ message = e.Message;
+ return false;
+ }
+ finally
+ {
+ var remains = Get();
+ foreach (var item in remains)
+ {
+ Remove(item);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Model.Interface;
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// BadgeEventNotifier provides APIs regarding app badge information.
+ /// </summary>
+ public class BadgeEventNotifier : IBadgeEventNotifier
+ {
+ private static readonly Lazy<BadgeEventNotifier> lazy
+ = new Lazy<BadgeEventNotifier>(() => new BadgeEventNotifier());
+
+ /// <summary>
+ /// An instance of BadgeEventNotifier.
+ /// </summary>
+ public static IBadgeEventNotifier Instance
+ {
+ get => lazy.Value;
+ }
+
+ private BadgeEventNotifier()
+ {
+ }
+
+
+ /// <summary>
+ /// Register an event handler to get updated bade notifications.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A registration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool Register(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ DependencyService.Get<IBadgeEventNotifier>().Register(handler);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A deregistration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ public bool DeRegister(EventHandler<BadgeUpdateEventArgs> handler)
+ {
+ DependencyService.Get<IBadgeEventNotifier>().DeRegister(handler);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Get badge information immediately.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ /// <returns>A badge information</returns>
+ /// <see cref="BadgeInformation"/>
+ public BadgeInformation Get(string appId)
+ {
+ return DependencyService.Get<IBadgeEventNotifier>().Get(appId);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using System;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// An event arguments for the badge update notification.
+ /// </summary>
+ public class BadgeUpdateEventArgs : EventArgs
+ {
+ /// <summary>
+ /// An updated badge information.
+ /// </summary>
+ public BadgeInformation UpdatedInformation
+ {
+ get; set;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Linq;
+using Homescreen.Debug;
+using Xamarin.Forms;
+using Homescreen.DataModel;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// HomeWidgetProvider provides APIs to insert, delete, get, update widgets and
+ /// to set and get a number of widgets.
+ /// </summary>
+ public class HomeWidgetProvider
+ {
+ private static readonly Lazy<HomeWidgetProvider> lazy
+ = new Lazy<HomeWidgetProvider>(() => new HomeWidgetProvider());
+
+ /// <summary>
+ /// An instance of the HomeWidgetProvider.
+ /// </summary>
+ public static HomeWidgetProvider Instance
+ {
+ get => lazy.Value;
+ }
+
+ /// <summary>
+ /// An instance of the HomescreenDataStorage which manages a storage.
+ /// </summary>
+ protected HomescreenDataStorage StorageInstance { get; set; }
+
+ /// <summary>
+ /// A constructor of the HomeWidgetProvider that initiates StorageInstance and
+ /// checks the existence of table for widgets
+ /// </summary>
+ protected HomeWidgetProvider()
+ {
+ StorageInstance = HomescreenDataStorage.Instance;
+
+ if (StorageInstance.TableExists<WidgetInformation>() == false)
+ {
+ InsertDefaultData();
+ }
+ }
+
+ /// <summary>
+ /// A method makes a default widget
+ /// </summary>
+ protected void InsertDefaultData()
+ {
+ Log.Debug("Insert Default data");
+ WidgetInformation widget = new WidgetInformation()
+ {
+ PackageId = "org.tizen.calendar",
+ Name = "calendar",
+ WidgetId = "org.tizen.calendar.widget",
+ Type = WidgetInformation.SizeType.SIZE_4x4,
+ PageIndex = 0,
+ X = 0,
+ Y = 0,
+ };
+ SetWidgetPageCount(2);
+ InsertWidget(widget);
+#if false //For TEST
+ WidgetInformation widget2 = new WidgetInformation()
+ {
+ PackageId = "org.tizen.contacts",
+ Title = "contacts",
+
+ WidgetId = "org.tizen.contacts.widget",
+ PageIndex = 1,
+ X = 0,
+ Y = 0,
+
+ Type = WidgetSizeType.SIZE_4x2,
+ };
+ InsertWidget(widget2);
+ WidgetInformation widget3 = new WidgetInformation()
+ {
+ PackageId = "org.tizen.contacts",
+ Title = "contacts",
+
+ WidgetId = "org.tizen.contacts.widget",
+ PageIndex = 1,
+ X = 0,
+ Y = 2,
+
+ Type = WidgetSizeType.SIZE_4x2,
+ };
+ InsertWidget(widget3);
+#endif
+ }
+
+ /// <summary>
+ /// A method inserts a widget information to the storage.
+ /// </summary>
+ /// <param name="widgetInfo">A widget information</param>
+ /// <returns>A widget information ID</returns>
+ public long InsertWidget(WidgetInformation widgetInfo)
+ {
+ return StorageInstance.Insert<WidgetInformation>(widgetInfo);
+ }
+
+ /// <summary>
+ /// A method deletes a widget information from the storage
+ /// </summary>
+ /// <param name="widgetInfo">A widget information will be deleted.</param>
+ public void DeleteWidget(WidgetInformation widgetInfo)
+ {
+ DependencyService.Get<IRemoteViewStorage>().Delete(widgetInfo.Id);
+ StorageInstance.Delete<WidgetInformation>(widgetInfo);
+ }
+
+ /// <summary>
+ /// A method deletes all widget information in the storage
+ /// </summary>
+ public void DeleteWidgetAll()
+ {
+ StorageInstance.DeleteAll<WidgetInformation>();
+ }
+
+ /// <summary>
+ /// A method update a widget information with updates.
+ /// </summary>
+ /// <param name="widgetInfo">An updated widget information</param>
+ public void UpdateWidget(WidgetInformation widgetInfo)
+ {
+ Log.Debug($"UpdateWidget : called :{widgetInfo.WidgetId} ");
+ StorageInstance.Update<WidgetInformation>(widgetInfo);
+ }
+
+ /// <summary>
+ /// A method provides widgets with matching a page index.
+ /// </summary>
+ /// <param name="pageIndex">A page index</param>
+ /// <returns>A widget list</returns>
+ public IList<WidgetInformation> GetWidgetList(int pageIndex)
+ {
+ return StorageInstance.Query<WidgetInformation>
+ ("SELECT * FROM WidgetInformation WHERE PageIndex = ?", pageIndex);
+ }
+
+ /// <summary>
+ /// A method provides a number of widget pages.
+ /// </summary>
+ /// <returns>A number of widget pages</returns>
+ public int GetWidgetPageCount()
+ {
+ IEnumerable<WidgetPageCountInformation> list = StorageInstance.Read<WidgetPageCountInformation>();
+
+ if (list == null || list.Count() == 0)
+ {
+ return 0;
+ }
+
+ return list.ElementAt<WidgetPageCountInformation>(0).PageCount;
+ }
+
+ /// <summary>
+ /// A method sets a number of widget pages.
+ /// This method should be called if a new widget page is created.
+ /// </summary>
+ /// <param name="pageCount">A number of widget pages</param>
+ public void SetWidgetPageCount(int pageCount)
+ {
+ WidgetPageCountInformation info;
+ IEnumerable<WidgetPageCountInformation> list = StorageInstance.Read<WidgetPageCountInformation>();
+
+ Log.Debug(list == null ? "NULL" : $"Count = {list.Count()} -> {pageCount}");
+
+ if (list == null || list.Count() == 0 || (info = list.ElementAt<WidgetPageCountInformation>(0)) == null)
+ {
+ info = new WidgetPageCountInformation()
+ {
+ PageCount = 0,
+ };
+ }
+
+ info.PageCount = pageCount;
+
+ StorageInstance.Update<WidgetPageCountInformation>(info);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 SQLite;
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Homescreen.DataModel;
+using Homescreen.Model.Interface;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// HomescreenDataStorage has APIs to manage a physical storage.
+ /// </summary>
+ public class HomescreenDataStorage
+ {
+ private static readonly Lazy<HomescreenDataStorage> lazy
+ = new Lazy<HomescreenDataStorage>(() => new HomescreenDataStorage());
+
+ /// <summary>
+ /// A SQLite connection which is a main interface.
+ /// </summary>
+ protected SQLiteConnection connection;
+
+ /// <summary>
+ /// An instance of HomescreenDataStorage
+ /// </summary>
+ public static HomescreenDataStorage Instance
+ {
+ get => lazy.Value;
+ }
+
+ /// <summary>
+ /// A constructor of HomescreenDataStroage.
+ /// </summary>
+ protected HomescreenDataStorage()
+ {
+ connection = DependencyService.Get<ISQLite>().GetConnection();
+ }
+
+ /// <summary>
+ /// A method checks the table existence.
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table.</typeparam>
+ /// <returns>An existence of table for the DB item.</returns>
+ public bool TableExists<T>() where T : DataStorageObject
+ {
+ var tableInfo = connection.GetTableInfo(typeof(T).Name);
+ if (tableInfo.Count == 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// A method inserts a DB item to the storage.
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <param name="item">A DB item to be inserted.</param>
+ /// <returns>An inserted DB item's ID</returns>
+ public long Insert<T>(T item) where T : DataStorageObject
+ {
+ if (TableExists<T>() == false)
+ {
+ DependencyService.Get<ISQLite>().CreateTable<T>(connection);
+ }
+
+ long id = -1;
+ connection.RunInTransaction(() =>
+ {
+ connection.Insert(item);
+ id = connection.ExecuteScalar<int>("select last_insert_rowid();");
+ });
+ return id;
+ }
+
+ /// <summary>
+ /// A method deletes a DB item from the storage.
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <param name="item">A DB item to be deleted.</param>
+ public void Delete<T>(T item) where T : DataStorageObject
+ {
+ if (TableExists<T>() == false)
+ {
+ return;
+ }
+
+ connection.Delete<T>(item.Id);
+ }
+
+ /// <summary>
+ /// A method query DB items with a SQL statement.
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <param name="query">A SQL statement</param>
+ /// <param name="args">An arguments of the SQL statement</param>
+ /// <returns>A queried DB items.</returns>
+ public IList<T> Query<T>(string query, params object[] args) where T : DataStorageObject, new()
+ {
+ if (TableExists<T>() == false)
+ {
+ return new List<T>();
+ }
+
+ return connection.Query<T>(query, args);
+ }
+
+ /// <summary>
+ /// A method provides all matching typed DB items in the Storage
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <returns>A matching typed DB items.</returns>
+ public IEnumerable<T> Read<T>() where T : DataStorageObject, new()
+ {
+ if (TableExists<T>() == false)
+ {
+ return new List<T>();
+ }
+
+ return connection.Table<T>();
+ }
+
+ /// <summary>
+ /// A method provides a matching DB item in the Storage
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <param name="primaryKey">A finding DB item ID</param>
+ /// <returns>A found DB item</returns>
+ public T Read<T>(object primaryKey) where T : DataStorageObject, new()
+ {
+ if (TableExists<T>() == false)
+ {
+ return new T();
+ }
+
+ return connection.Get<T>(primaryKey);
+ }
+
+ /// <summary>
+ /// A method updates a DB item.
+ /// </summary>
+ /// <typeparam name="T">A type of DB item which selects a table</typeparam>
+ /// <param name="item">An updated DB item.</param>
+ /// <returns>An updated item's ID</returns>
+ public long Update<T>(T item) where T : DataStorageObject
+ {
+ int ret;
+ if (TableExists<T>() == false)
+ {
+ return Insert<T>(item);
+ }
+
+ ret = connection.Update(item);
+ if (ret == 0)
+ {
+ return Insert<T>(item);
+ }
+
+ return item.Id;
+ }
+
+ public void DeleteAll<T>() where T : DataStorageObject
+ {
+ if (TableExists<T>() == false)
+ {
+ return;
+ }
+
+ connection.DeleteAll<T>();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface for app launching
+ /// </summary>
+ public interface IAppLauncher
+ {
+ /// <summary>
+ /// A method launches an app which matched with the appId.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ void LaunchApp(string appId);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Model;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface is used for managing item information with a storage.
+ /// </summary>
+ /// <see cref="AppsDataProvider"/>
+ public interface IAppsDataProvider
+ {
+ /// <summary>
+ /// Add item information to the storage.
+ /// </summary>
+ /// <param name="item">An item information which is describing an app or a folder.</param>
+ /// <returns>An updated item ID</returns>
+ long Set(ItemInformation item);
+
+ /// <summary>
+ /// Update item information.
+ /// </summary>
+ /// <param name="item">An item information contains updated information.</param>
+ /// <returns>An updated item ID</returns>
+ long Update(ItemInformation item);
+
+ /// <summary>
+ /// Add items information to the storage.
+ /// </summary>
+ /// <param name="items">An items information</param>
+ void Set(IList<ItemInformation> items);
+
+ /// <summary>
+ /// Provides stored items information.
+ /// </summary>
+ /// <returns>An items information list</returns>
+ IList<ItemInformation> Get();
+
+ /// <summary>
+ /// Remove an item from the storage
+ /// </summary>
+ /// <param name="item">An item information to be removed</param>
+ void Remove(ItemInformation item);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using System;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface regarding app's badge notification.
+ /// </summary>
+ public interface IBadgeEventNotifier
+ {
+ /// <summary>
+ /// Register an event handler to get updated badge notifications.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A registration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ bool Register(EventHandler<BadgeUpdateEventArgs> handler);
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler</param>
+ /// <returns>A deregistration status, if succeed will return true.</returns>
+ /// <see cref="BadgeUpdateEventArgs"/>
+ bool DeRegister(EventHandler<BadgeUpdateEventArgs> handler);
+
+ /// <summary>
+ /// Provides badge information.
+ /// </summary>
+ /// <param name="appId">An app ID</param>
+ /// <returns>A badge information</returns>
+ /// <see cref="BadgeInformation"/>
+ BadgeInformation Get(string appId);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel
+{
+ /// <summary>
+ /// Status bar mode.
+ /// </summary>
+ public enum StatusBarMode
+ {
+ /// <summary>
+ /// Make the status bar be opacified
+ /// </summary>
+ Opaque,
+
+ /// <summary>
+ /// Make the status bar be translucent
+ /// </summary>
+ Translucent,
+
+ /// <summary>
+ /// Make the status bar be transparent
+ /// </summary>
+ Transparent,
+ }
+
+ /// <summary>
+ /// An interface related with a Tizen device
+ /// </summary>
+ public interface IDeviceInfo
+ {
+ /// <summary>
+ /// A width of device screen size
+ /// </summary>
+ int Width { get; }
+
+ /// <summary>
+ /// A height of device screen size
+ /// </summary>
+ int Height { get; }
+
+ /// <summary>
+ /// Mode of status bar displaying
+ /// </summary>
+ /// <see cref="StatusBarMode"/>
+ StatusBarMode StatusBarMode { get; set; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface regarding package modification events
+ /// </summary>
+ public interface IPackageChanged
+ {
+ /// <summary>
+ /// Register an event handler to get package modification events
+ /// </summary>
+ /// <param name="handler">An event handler will handle the package modification events</param>
+ /// <returns>A registration status</returns>
+ bool Register(EventHandler<PackageUpdateEventArgs> handler);
+
+ /// <summary>
+ /// Deregister an event handler
+ /// </summary>
+ /// <param name="handler">An event handler to be deregistered.</param>
+ /// <returns>A deregistration status</returns>
+ bool DeRegister(EventHandler<PackageUpdateEventArgs> handler);
+
+ /// <summary>
+ /// Get all installed apps information.
+ /// </summary>
+ /// <returns>An installed app information list</returns>
+ Task<IList<InstalledAppInformation>> GetAllInstalledAppInformation();
+
+ /// <summary>
+ /// Get an installed app information matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>A matching app information.</returns>
+ InstalledAppInformation GetInstalledAppInformation(string applicationId);
+
+ /// <summary>
+ /// Uninstall an app matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>An uninstall status</returns>
+ bool RequestUninstall(string applicationId);
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.Model
+{
+ public interface IRemoteViewStorage
+ {
+ void Delete(long widgetID);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 SQLite;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface regarding SQLite
+ /// </summary>
+ public interface ISQLite
+ {
+ /// <summary>
+ /// Provides SQLite connection which is main interface for data basing.
+ /// </summary>
+ /// <returns>A connection of SQLite</returns>
+ SQLiteConnection GetConnection();
+
+ /// <summary>
+ /// Create table
+ /// </summary>
+ /// <typeparam name="T">A DB item type that will be used to make a table.</typeparam>
+ /// <param name="conn">A SQLite connection</param>
+ void CreateTable<T>(SQLiteConnection conn);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+
+namespace Homescreen.Model.Interface
+{
+ /// <summary>
+ /// An interface regarding wallpaper information getting and updating.
+ /// </summary>
+ public interface IWallpaperChanged
+ {
+ /// <summary>
+ /// Get wallpaper full path.
+ /// </summary>
+ /// <returns>a wallpaper path</returns>
+ string GetWallpaperPath();
+
+ /// <summary>
+ /// Register an event handler to receive wallpaper update events.
+ /// </summary>
+ /// <param name="handler">An event handler will receive wallpaper update events</param>
+ void RegisterWallpaperChangedCallback(EventHandler handler);
+
+ /// <summary>
+ /// Deregister an event handler.
+ /// </summary>
+ /// <param name="handler">An event handler will be deregistered.</param>
+ void DeregisterWallpaperChangedCallback(EventHandler handler);
+
+ /// <summary>
+ /// Launch wallpaper settings to replace with another one.
+ /// </summary>
+ void LaunchWallpaperSetting();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using System.Collections.Generic;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// An interface regarding widget
+ /// </summary>
+ public interface IWidgetManager
+ {
+ /// <summary>
+ /// A widget list property.
+ /// </summary>
+ List<WidgetInformation> WidgetList { get; }
+
+ /// <summary>
+ /// Get widgets information matching widgetId
+ /// </summary>
+ /// <param name="widgetId">A widget ID</param>
+ /// <returns>A list of widgets information</returns>
+ List<WidgetInformation> GetWidgetInfo(string widgetId);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Model.Interface;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// PackageChangedNotifier manages registration of event handler for the package update,
+ /// provides installed package list and a way to uninstall an app.
+ /// </summary>
+ public class PackageChangedNotifier : IPackageChanged
+ {
+ private static readonly Lazy<PackageChangedNotifier> lazy
+ = new Lazy<PackageChangedNotifier>(() => new PackageChangedNotifier());
+
+ /// <summary>
+ /// An instance of PackageChangedNotifier.
+ /// </summary>
+ public static IPackageChanged Instance
+ {
+ get => lazy.Value;
+ }
+
+ private PackageChangedNotifier()
+ {
+
+ }
+
+ /// <summary>
+ /// Register an event handler to get package modification events
+ /// </summary>
+ /// <param name="handler">An event handler will handle the package modification events</param>
+ /// <returns>A registration status</returns>
+ public bool Register(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ DependencyService.Get<IPackageChanged>().Register(handler);
+ return true;
+ }
+
+ /// <summary>
+ /// Deregister an event handler
+ /// </summary>
+ /// <param name="handler">An event handler to be deregistered.</param>
+ /// <returns>A deregistration status</returns>
+ public bool DeRegister(EventHandler<PackageUpdateEventArgs> handler)
+ {
+ DependencyService.Get<IPackageChanged>().DeRegister(handler);
+ return true;
+ }
+
+ /// <summary>
+ /// Get all installed apps information.
+ /// </summary>
+ /// <returns>An installed app information list</returns>
+ public Task<IList<InstalledAppInformation>> GetAllInstalledAppInformation()
+ {
+ return DependencyService.Get<IPackageChanged>().GetAllInstalledAppInformation();
+ }
+
+ /// <summary>
+ /// Get an installed app information matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>A matching app information.</returns>
+ public InstalledAppInformation GetInstalledAppInformation(string applicationId)
+ {
+ return DependencyService.Get<IPackageChanged>().GetInstalledAppInformation(applicationId);
+ }
+
+ /// <summary>
+ /// Uninstall an app matching with applicationId
+ /// </summary>
+ /// <param name="applicationId">An app ID</param>
+ /// <returns>An uninstall status</returns>
+ public bool RequestUninstall(string applicationId)
+ {
+ return DependencyService.Get<IPackageChanged>().RequestUninstall(applicationId);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// An enumeration of types for the package updating.
+ /// </summary>
+ public enum PackageUpdateType
+ {
+ /// <summary>
+ /// A package is newly installed.
+ /// </summary>
+ Installed,
+
+ /// <summary>
+ /// A package is uninstalled.
+ /// </summary>
+ Uninstalled,
+
+ /// <summary>
+ /// A package is updated
+ /// </summary>
+ Updated,
+ }
+
+ /// <summary>
+ /// An event argument for packaging updating.
+ /// </summary>
+ public class PackageUpdateEventArgs : EventArgs
+ {
+ /// <summary>
+ /// A package ID
+ /// </summary>
+ public string PackageId
+ {
+ set; get;
+ }
+
+ /// <summary>
+ /// A type of package updating event.
+ /// </summary>
+ public PackageUpdateType Type
+ {
+ set; get;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model.Interface;
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.Model
+{
+ /// <summary>
+ /// WallpaperChangedNotifier provides current wallpaper information and
+ /// a way to register to the wallpaper managing subsystem.
+ /// </summary>
+ public class WallpaperChangedNotifier : IWallpaperChanged
+ {
+ private static readonly Lazy<WallpaperChangedNotifier> lazy
+ = new Lazy<WallpaperChangedNotifier>(() => new WallpaperChangedNotifier());
+
+ /// <summary>
+ /// An instance of WallpaperChangedNotifier.
+ /// </summary>
+ public static IWallpaperChanged Instance
+ {
+ get => lazy.Value;
+ }
+
+ private WallpaperChangedNotifier()
+ {
+ }
+
+ /// <summary>
+ /// Deregister wallpaper changed event.
+ /// </summary>
+ /// <param name="handler">An event handler to be deregistered</param>
+ public void DeregisterWallpaperChangedCallback(EventHandler handler)
+ {
+ DependencyService.Get<IWallpaperChanged>().DeregisterWallpaperChangedCallback(handler);
+ }
+
+ /// <summary>
+ /// Get a full path of currently displaying wallpaper.
+ /// </summary>
+ /// <returns>A wallpaper file full path.</returns>
+ public string GetWallpaperPath()
+ {
+ return DependencyService.Get<IWallpaperChanged>().GetWallpaperPath();
+ }
+
+ /// <summary>
+ /// Register an event handler for the wallpaper changed event.
+ /// </summary>
+ /// <param name="handler">An event handler for the wallpaper update notification.</param>
+
+ public void RegisterWallpaperChangedCallback(EventHandler handler)
+ {
+ DependencyService.Get<IWallpaperChanged>().RegisterWallpaperChangedCallback(handler);
+ }
+
+ /// <summary>
+ /// Launch a wallpaper setting app to update the wallpaper.
+ /// </summary>
+ public void LaunchWallpaperSetting()
+ {
+ DependencyService.Get<IWallpaperChanged>().LaunchWallpaperSetting();
+ }
+ }
+}
--- /dev/null
+<StyleCopSettings Version="105">
+ <GlobalSettings>
+ <StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
+ </GlobalSettings>
+ <Analyzers>
+ <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EnumerationItemsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustContainValidXml">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VoidReturnValueMustNotBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustMatchAccessors">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustNotBeEmpty">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustContainWhitespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustMeetCharacterPercentage">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationHeadersMustNotContainBlankLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludedDocumentationXPathDoesNotExist">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InheritDocMustBeUsedWithInheritingClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustBeSpelledCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMustHaveHeader">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustShowCopyright">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveCopyrightText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustContainFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveValidCompanyText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
+ <Rules>
+ <Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotContainUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotUseHungarianNotation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VariableNamesMustNotBePrefixed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotBeginWithUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeadersMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleBlankLinesInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ChainedStatementBlocksMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="WhileDoFooterMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeSeparatedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtStartOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtEndOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules">
+ <Rules>
+ <Rule Name="AccessModifierMustBeDeclared">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldsMustBePrivate">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeAnalysisSuppressionMustHaveJustification">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugAssertMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugFailMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ArithmeticExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConditionalExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveDelegateParenthesisWhenPossible">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AttributeConstructorMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveUnnecessaryCode">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
+ <Rules>
+ <Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustAppearInTheCorrectOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeOrderedByAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstantsMustAppearBeforeFields">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticElementsMustAppearBeforeInstanceElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DeclarationKeywordsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ProtectedMustComeBeforeInternal">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertyAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EventAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyElementsMustAppearBeforeStaticNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InstanceReadonlyElementsMustAppearBeforeInstanceNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NoValueFirstComparison">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SystemUsingDirectivesMustBePlacedBeforeOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingDirectivesMustBeOrderedAlphabeticallyByNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingStaticDirectivesMustBePlacedAfterUsingNamespaceDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="CommentsMustContainText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixLocalCallsWithThis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixCallsCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeOnDeclarationLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommaMustBeOnSameLineAsPreviousParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterListMustFollowDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustFollowComma">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustNotSpanMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustFollowPreviousClause">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPlaceRegionsWithinElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainEmptyStatements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedComments">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedRegions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseStringEmptyForEmptyStrings">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseBuiltInTypeAlias">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseShorthandForNullableTypes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.SpacingRules">
+ <Rules>
+ <Rule Name="CommasMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SemicolonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationLinesMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PreprocessorKeywordsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OperatorKeywordMustBeFollowedBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NullableTypeSymbolsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="MemberAccessSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncrementDecrementSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NegativeSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PositiveSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ColonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="TabsMustNotBeUsed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotSplitNullConditionalOperators">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:apps="clr-namespace:Homescreen.View.Apps"
+ xmlns:view="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Apps.AppsLayout"
+ AppsState="{Binding AppsState}"
+ LaunchAppCommand="{Binding LaunchAppCommand}"
+ ChooseAppCommand="{Binding ChooseAppCommand}">
+
+ <ContentView.Content>
+ <RelativeLayout
+ x:Name="Container">
+ <apps:AppsPageScrollView
+ x:Name="pageScroller"
+ Opacity="0.0"
+ ItemsSource="{Binding AppsInformation}"
+ ChoosedFolder="{Binding ChoosedFolderInformation}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+
+ <apps:ChooserTopBar
+ x:Name="chooserBar"
+ IsVisible="False"
+ ChoosedApps="{Binding ChoosedAppsInformation}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.086, Constant=0}"/>
+
+ <view:OptionButton
+ x:Name="menuButton"
+ Opacity="0.0"
+ IconImageSource="home_button_menu.png"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.04444444, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.928125, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.18055555, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.059375, Constant=0}"/>
+
+ <view:OptionButton
+ x:Name="widgetsButton"
+ Opacity="0.0"
+ IconImageSource="home_button_home.png"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.775, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.928125, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.18055555, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.059375, Constant=0}"/>
+
+ <apps:FolderLayout
+ x:Name="Folder"
+ IsVisible="{Binding IsFolderVisible, Mode=TwoWay}"
+ LaunchAppCommand="{Binding LaunchAppCommand}"
+ ChooseStartCommand="{Binding ChooseStartCommand}"
+ OpenedFolder="{Binding OpenedFolderInformation}"
+ ChoosedApps="{Binding ChoosedAppsInformation}"
+ FolderTextChangedCommand="{Binding FolderTextChangedCommand}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+
+ </RelativeLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.View.Interface;
+using System.Runtime.CompilerServices;
+using Homescreen.DataModel;
+using Homescreen.Debug;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// AppsLayout is a base layout for the Apps page.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AppsLayout : ContentView, IVisibilityChanged, IAppsLayout, IAppsStateHandler
+ {
+ private IDictionary<AppsState, IAppsState> States = new Dictionary<AppsState, IAppsState>();
+ private IAppsState previousState;
+ /// <summary>
+ /// A Current app state property
+ /// </summary>
+ public IAppsState CurrentState { get; private set; }
+
+ #region IAppsLayout implementation
+
+ /// <summary>
+ /// The Apps main layout
+ /// </summary>
+ public AppsLayout MainLayout => this;
+
+ /// <summary>
+ /// The Menu button which shows menus for the Apps page.
+ /// </summary>
+ public OptionButton MenuButton => menuButton;
+
+ /// <summary>
+ /// The Widget button which switch to the Widgets page.
+ /// </summary>
+ public OptionButton WidgetsButton => widgetsButton;
+
+ /// <summary>
+ /// The Apps Page scroll view that contains AppsPageLayouts.
+ /// </summary>
+ public AppsPageScrollView PageScroller => pageScroller;
+
+ /// <summary>
+ /// The Chooser bar which will be shown with "Done" and "Cancel" buttons on top of screen during the Choose mode.
+ /// </summary>
+ public ChooserTopBar ChooserBar => chooserBar;
+
+ /// <summary>
+ /// The Folder view which shows a folder's name and apps in it.
+ /// </summary>
+ public FolderLayout FolderView => Folder;
+
+ #endregion
+
+ #region Binding Properties
+
+ /// <summary>
+ /// A bind-able property for AppsState.
+ /// </summary>
+ public static readonly BindableProperty AppsStateProperty
+ = BindableProperty.Create("AppsState", typeof(AppsState),
+ typeof(AppsLayout), default(AppsState), BindingMode.TwoWay);
+ /// <summary>
+ /// AppsState property which shows current Apps page's state
+ /// </summary>
+ public AppsState AppsState
+ {
+ get { return (AppsState)GetValue(AppsStateProperty); }
+ set { SetValue(AppsStateProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for ChooseAppCommand.
+ /// </summary>
+ public static readonly BindableProperty ChooseAppCommandProperty
+ = BindableProperty.Create("ChooseAppCommand", typeof(Command), typeof(AppsPageLayout));
+ /// <summary>
+ /// ChooseAppCommand will be which will be used in the Choose mode.
+ /// </summary>
+ public Command ChooseAppCommand
+ {
+ get => (Command)GetValue(ChooseAppCommandProperty);
+ set => SetValue(ChooseAppCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for LaunchAppCommand.
+ /// </summary>
+ public static readonly BindableProperty LaunchAppCommandProperty
+ = BindableProperty.Create("LaunchAppCommand", typeof(Command), typeof(AppsPageLayout));
+ /// <summary>
+ /// LaunchAppCommand which will be used to launch an app.
+ /// </summary>
+ public Command LaunchAppCommand
+ {
+ get => (Command)GetValue(LaunchAppCommandProperty);
+ set => SetValue(LaunchAppCommandProperty, value);
+ }
+ #endregion
+
+ /// <summary>
+ /// A constructor of AppsLayout which will init the Apps page state and commands.
+ /// </summary>
+ public AppsLayout()
+ {
+ InitializeComponent();
+ BindingContext = new AppsInformationCenter();
+
+ SetState(GetNormalState());
+
+ GetEditState();
+ GetChooseState();
+ GetDragState();
+
+ PageScroller.FolderOpenCommand = new Command<FolderInformation>((information) =>
+ {
+ Log.Debug($"{information.Id}: {information.Label} : Folder Open");
+
+ Folder.IsVisible = true;
+ Folder.OpenedFolder = information;
+ });
+
+ PageScroller.PageScrollView.LongPressed += (s, e) =>
+ {
+ Log.Debug("PageScrollView : long pressed");
+ if (CurrentState.State == AppsState.Normal)
+ {
+ SetState(GetEditState());
+ }
+ };
+ PageScroller.FolderView = Folder;
+
+ Folder.ScrollviewMouseDown = PageScroller.ItemMouseDown;
+ Folder.ScrollviewMouseUp = PageScroller.ItemMouseUp;
+ Folder.ScrollviewMouseHold = PageScroller.ItemMouseHold;
+ Folder.ScrollviewMouseMove = PageScroller.ItemMouseMove;
+ }
+
+ /// <summary>
+ /// A method to hide the Apps page.
+ /// </summary>
+ /// <param name="animation">A flag to turn on and off the animation while hiding.</param>
+ public void Hide(bool animation = true)
+ {
+ if (CurrentState.State != AppsState.Normal)
+ {
+ SetState(GetNormalState());
+ }
+
+ PageScroller.HideIndicator();
+
+ Action<double, bool> finished = (v, c) =>
+ {
+ Opacity = 0.0;
+ IsVisible = false;
+ };
+
+ if (animation)
+ {
+ new Animation
+ {
+ { 0, 0.8, new Animation(v => PageScroller.Opacity = v, 1, 0, Easing.CubicInOut) },
+ { 0, 0.8, new Animation(v => PageScroller.Scale = v, 1, 0.8, Easing.CubicInOut) },
+ { 0, 0.8, new Animation(v => PageScroller.TranslationY = v, 0, (Height * 0.1), Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => MenuButton.Scale = v, 1, 0.5, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => WidgetsButton.Scale = v, 1, 0.5, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => MenuButton.Opacity = v, 1, 0, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => WidgetsButton.Opacity = v, 1, 0, Easing.CubicInOut) },
+ }.Commit(this, "HideAppsLayout", 16, 300, null, finished);
+ }
+ else
+ {
+ finished(0, false);
+ }
+ }
+
+ /// <summary>
+ /// A method to show the Apps page.
+ /// </summary>
+ /// <param name="animation">A flag to turn on and off the animation while hiding.</param>
+ public void Show(bool animation = true)
+ {
+ if (CurrentState.State != AppsState.Normal)
+ {
+ SetState(GetNormalState());
+ }
+
+ Opacity = 1.0;
+ IsVisible = true;
+
+ Action<double, bool> finished = (v, c) =>
+ {
+ PageScroller.ShowIndicator();
+ };
+
+ if (animation)
+ {
+ new Animation
+ {
+ { 0.1, 1, new Animation(v => PageScroller.Opacity = v, 0, 1, Easing.SinInOut) },
+ { 0.1, 1, new Animation(v => PageScroller.Scale = v, 0.8, 1, Easing.SinInOut) },
+ { 0.1, 1, new Animation(v => PageScroller.TranslationY = v, (Height * 0.1), 0, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => MenuButton.Scale = v, 0.5, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => WidgetsButton.Scale = v, 0.5, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => MenuButton.Opacity = v, 0, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => WidgetsButton.Opacity = v, 0, 1, Easing.SinInOut) },
+ }.Commit(this, "ShowAppsLayout", 16, 300, null, finished);
+ }
+ else
+ {
+ finished(0, false);
+ }
+ }
+
+ /// <summary>
+ /// A back key handling method
+ /// </summary>
+ public void OnBackKeyPressed()
+ {
+ Log.Debug("Apps Layout, Back, " + AppsState + ", " + CurrentState);
+
+ CurrentState.OnBack();
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed
+ /// </summary>
+ public void OnMenuKeyPressed()
+ {
+ Log.Debug("Apps Layout, Menu, " + AppsState + ", " + CurrentState);
+
+ CurrentState.OnMenu();
+ }
+
+ /// <summary>
+ /// A home key handling method
+ /// </summary>
+ public void OnHomeKeyPressed()
+ {
+ CurrentState.OnHome();
+ }
+
+ /// <summary>
+ /// A method will be called when any property in the view is changed
+ /// </summary>
+ /// <param name="propertyName">A changed property's name</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == AppsStateProperty.PropertyName)
+ {
+ Log.Debug($"State will be Changed to {AppsState}");
+ if (States.TryGetValue(AppsState, out IAppsState state))
+ {
+ Log.Debug($"State is Changed to {state}");
+ SetState(state);
+ }
+ }
+ }
+
+ #region IAppsStateHandler implementation
+
+ /// <summary>
+ /// A method provides an Apps Normal state.
+ /// </summary>
+ /// <returns>A Normal State</returns>
+ public IAppsState GetNormalState()
+ {
+ if (States.TryGetValue(AppsState.Normal, out IAppsState state))
+ {
+ return state;
+ }
+
+ state = new NormalState(this, this);
+ States.Add(AppsState.Normal, state);
+ return state;
+ }
+
+ /// <summary>
+ /// A method provides an Apps Edit state.
+ /// </summary>
+ /// <returns>An Edit State</returns>
+ public IAppsState GetEditState()
+ {
+ if (States.TryGetValue(AppsState.Edit, out IAppsState state))
+ {
+ return state;
+ }
+
+ state = new EditState(this, this);
+ States.Add(AppsState.Edit, state);
+ return state;
+ }
+
+ /// <summary>
+ /// A method provides an Apps Drag state.
+ /// </summary>
+ /// <returns>A Drag State</returns>
+ public IAppsState GetDragState()
+ {
+ if (States.TryGetValue(AppsState.Drag, out IAppsState state))
+ {
+ return state;
+ }
+
+ state = new DragState(this, this);
+ States.Add(AppsState.Drag, state);
+ return state;
+ }
+
+ /// <summary>
+ /// A method provides an Apps Choose state.
+ /// </summary>
+ /// <returns>A Choose State</returns>
+ public IAppsState GetChooseState()
+ {
+ if (States.TryGetValue(AppsState.Choose, out IAppsState state))
+ {
+ return state;
+ }
+
+ state = new ChooseState(this, this);
+ States.Add(AppsState.Choose, state);
+ return state;
+ }
+
+ /// <summary>
+ /// A method provides a Previous state.
+ /// </summary>
+ /// <returns>A Previous State</returns>
+ public IAppsState GetPreviousState()
+ {
+ return previousState;
+ }
+
+ /// <summary>
+ /// A method to set new state
+ /// </summary>
+ /// <param name="newState">A new state</param>
+ public void SetState(IAppsState newState)
+ {
+ if (CurrentState == newState ||
+ newState == null)
+ {
+ Log.Debug($"Apps State SAME : Current is {CurrentState?.State}, Next is {newState?.State}");
+ return;
+ }
+
+ Log.Debug($"Apps State Changed : Current is {CurrentState?.State}, Next is {newState?.State}");
+
+ CurrentState?.StepOut();
+ previousState = CurrentState;
+ CurrentState = newState;
+ CurrentState.StepIn();
+
+ AppsState = newState.State;
+ Folder.AppsState = newState.State;
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.Apps.AppsPageLayout"
+ DeleteItemCommand="{Binding DeleteItemCommand}"
+ ChoosedApps="{Binding ChoosedAppsInformation}"
+ UpdatedItem="{Binding UpdatedItemId}">
+
+ <ContentView.Content>
+ <AbsoluteLayout x:Name="AppsPageAbsoluteLayout">
+ <BoxView x:Name="AppsBoxBG" Color="Black" Opacity="0.0"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All" />
+ <Grid x:Name="AppsPage"
+ AbsoluteLayout.LayoutBounds="0, 0.4, 1, 0.85"
+ AbsoluteLayout.LayoutFlags="All"
+ ColumnSpacing="0"
+ RowSpacing="0"
+ Padding="0"
+ Margin="0">
+ </Grid>
+ </AbsoluteLayout>
+
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.View.Event;
+using Homescreen.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// An AppsPagelayout which consists of App Items and Folder Items.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AppsPageLayout : ContentView
+ {
+ /// <summary>
+ /// max count of applications on the page.
+ /// </summary>
+ public static readonly int MaxNumberOfApps = 20;
+ private double gridCellWidth;
+ private double gridCellHeight;
+
+ private AppsState state;
+ /// <summary>
+ /// An Apps Page state
+ /// </summary>
+ public AppsState State
+ {
+ get => state;
+ set
+ {
+ state = value;
+
+ foreach (var item in AppsPage.Children)
+ {
+ if (item is ItemLayout itemLayout)
+ {
+ itemLayout.State = state;
+ }
+ }
+
+ switch (state)
+ {
+ case AppsState.Normal:
+ SetNormalstate();
+ break;
+ case AppsState.Edit:
+ SetEditState();
+ break;
+ case AppsState.Choose:
+ SetChooseState();
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// An information of selected folder in Choose mode
+ /// </summary>
+ public FolderInformation ChoosedFolder { get; set; }
+ public int PageIndex { get; set; }
+
+ public AppsPageScrollView PageScroller { get; set; }
+
+ public EventHandler ScrollViewMouseDown { get; set; }
+ public EventHandler ScrollViewMouseUp { get; set; }
+ public EventHandler ScrollViewMouseMove { get; set; }
+ public EventHandler ScrollViewMouseHold { get; set; }
+
+
+ /// <summary>
+ /// An event handler will be called when any item is clicked.
+ /// </summary>
+ public EventHandler<ItemClickedEventArgs> OnItemClicked;
+
+ #region Binding Properties
+
+ /// <summary>
+ /// A bind-able property for ItemsSource
+ /// </summary>
+ public static readonly BindableProperty ItemsSourceProperty
+ = BindableProperty.Create("ItemsSource", typeof(ObservableCollection<ItemInformation>),
+ typeof(AppsPageLayout), default(ObservableCollection<ItemInformation>));
+
+ /// <summary>
+ /// An ItemsSource property which represents current apps in this AppsPageLayout.
+ /// </summary>
+ public ObservableCollection<ItemInformation> ItemsSource
+ {
+ get { return (ObservableCollection<ItemInformation>)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for DeleteItemCommand
+ /// </summary>
+ public static readonly BindableProperty DeleteItemCommandProperty
+ = BindableProperty.Create("DeleteItemCommand", typeof(Command), typeof(AppsPageLayout));
+ /// <summary>
+ /// A DeleteItemCommand property which will be used for deleting an app or a folder.
+ /// </summary>
+ public Command DeleteItemCommand
+ {
+ get => (Command)GetValue(DeleteItemCommandProperty);
+ set => SetValue(DeleteItemCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ChoosedApps
+ /// </summary>
+ public static readonly BindableProperty ChoosedAppsProperty
+ = BindableProperty.Create("ChoosedApps", typeof(ICollection<ItemInformation>),
+ typeof(AppsPageLayout), default(ICollection<ItemInformation>));
+ /// <summary>
+ /// A ChoosedApps property which including selected apps in Choose mode.
+ /// </summary>
+ public ICollection<ItemInformation> ChoosedApps
+ {
+ get { return (ICollection<ItemInformation>)GetValue(ChoosedAppsProperty); }
+ set { SetValue(ChoosedAppsProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for UpdatedItem.
+ /// </summary>
+ public static readonly BindableProperty UpdatedItemProperty
+ = BindableProperty.Create("UpdatedItem", typeof(long), typeof(AppsPageLayout), default(long), BindingMode.TwoWay);
+
+ /// <summary>
+ /// An UpdatedItem property which will be used for UI update with adding DB id of updating apps.
+ /// </summary>
+ public long UpdatedItem
+ {
+ get { return (long)GetValue(UpdatedItemProperty); }
+ set { SetValue(UpdatedItemProperty, value); }
+ }
+ #endregion
+
+ /// <summary>
+ /// A constructor of AppsPageLayout.
+ /// </summary>
+ public AppsPageLayout()
+ {
+ InitializeComponent();
+
+ gridCellWidth = DependencyService.Get<IDeviceInfo>().Width * 0.99 / 4;
+ gridCellHeight = DependencyService.Get<IDeviceInfo>().Height * 0.85 / 5;
+
+ AppsPage.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridCellHeight) });
+ AppsPage.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridCellHeight) });
+ AppsPage.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridCellHeight) });
+ AppsPage.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridCellHeight) });
+ AppsPage.RowDefinitions.Add(new RowDefinition { Height = new GridLength(gridCellHeight) });
+
+ AppsPage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(gridCellWidth) });
+ AppsPage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(gridCellWidth) });
+ AppsPage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(gridCellWidth) });
+ AppsPage.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(gridCellWidth) });
+ }
+
+ private void SetApp()
+ {
+ AppsPage.Children.Clear();
+
+ for (int i = PageIndex * MaxNumberOfApps; i < ItemsSource.Count && i < (PageIndex + 1) * MaxNumberOfApps; i++)
+ {
+ MakeNewItem(i, ItemsSource[i]);
+ }
+ }
+
+ private void MakeNewItem(int index, ItemInformation item)
+ {
+ var itemLayout = new ItemLayout()
+ {
+ BindingContext = item,
+ State = State,
+ WidthRequest = gridCellWidth,
+ HeightRequest = gridCellHeight,
+
+ PageMouseDown = MouseDown,
+ PageMouseUp = MouseUp,
+ PageMouseHold = MouseHold,
+ PageMouseMove = MouseMove
+ };
+
+ itemLayout.OnItemClicked += ((sender, args) =>
+ {
+ Log.Debug("AppsPageLayout, itemLayout.ItemClicked");
+ OnItemClicked?.Invoke(sender, args);
+ });
+
+ itemLayout.DeleteItemCommand = new Command((information) =>
+ {
+ DeleteItemCommand.Execute(information);
+ });
+
+ itemLayout.UpdateIcon();
+ index = index % MaxNumberOfApps;
+ AppsPage.Children.Add(itemLayout, index % 4, index / 4);
+ }
+
+ private void SetChooseState()
+ {
+ this.ScaleTo(0.9, 300, Easing.Linear);
+ AppsBoxBG.FadeTo(0.3, 300, Easing.Linear);
+ AppsBoxBG.LayoutTo(AppsPage.Bounds, 300, Easing.Linear);
+ }
+
+ private async void SetEditState()
+ {
+ if (Scale != 0.9)
+ {
+ AppsBoxBG.Layout(AppsPageAbsoluteLayout.Bounds);
+ await Task.WhenAll(
+ this.ScaleTo(0.9, 300, Easing.Linear),
+ AppsBoxBG.FadeTo(0.3, 300, Easing.Linear),
+ AppsBoxBG.LayoutTo(AppsPage.Bounds, 300, Easing.Linear));
+
+ AppsBoxBG.Layout(AppsPage.Bounds);
+ AbsoluteLayout.SetLayoutBounds(AppsBoxBG, new Rectangle(0, 0.4, 1, 0.85));
+ }
+ }
+
+ private async void SetNormalstate()
+ {
+ await Task.WhenAll(
+ this.ScaleTo(1, 300, Easing.Linear),
+ AppsBoxBG.FadeTo(0, 300, Easing.Linear),
+ AppsBoxBG.LayoutTo(AppsPageAbsoluteLayout.Bounds, 300, Easing.Linear));
+ AbsoluteLayout.SetLayoutBounds(AppsBoxBG, new Rectangle(0, 0, 1, 1));
+ }
+
+ /// <summary>
+ /// A method will update ChoosedFolder property by newly selected folder.
+ /// </summary>
+ /// <param name="choosedFolder">A select folder information</param>
+ public void UpdateChoosedFolder(FolderInformation choosedFolder)
+ {
+ ChoosedFolder = choosedFolder;
+ foreach (var item in AppsPage.Children)
+ {
+ if (item is ItemLayout itemLayout &&
+ item.BindingContext is FolderInformation folderInfo)
+ {
+ if (folderInfo.Id == choosedFolder.Id)
+ {
+ Log.Debug("Disabled = " + folderInfo.Label + ", " + folderInfo.Id);
+ itemLayout.DisableClick();
+ }
+ else
+ {
+ Log.Debug("Enabled = " + folderInfo.Label + ", " + folderInfo.Id);
+ itemLayout.EnableClick();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method updates app icons in this AppsPageLayout
+ /// </summary>
+ public void UpdateIcon()
+ {
+ foreach (var item in AppsPage.Children)
+ {
+ if (item is ItemLayout itemLayout)
+ {
+ if (itemLayout.BindingContext is FolderInformation &&
+ (itemLayout.BindingContext as FolderInformation).HaveToUpdate == true)
+ {
+ itemLayout.UpdateIcon();
+ (itemLayout.BindingContext as FolderInformation).HaveToUpdate = false;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method updates a check-box of all apps in this AppsPageLayout
+ /// </summary>
+ /// <param name="itemId">A selected item id</param>
+ public void UpdateCheckBox(long itemId)
+ {
+ foreach (var item in AppsPage.Children)
+ {
+ if (item is ItemLayout itemLayout)
+ {
+ if ((itemLayout.BindingContext as ItemInformation).Id == itemId &&
+ (itemLayout.BindingContext is FolderInformation folder))
+ {
+ int checkCount = 0;
+ foreach (AppInformation child in folder.AppList)
+ {
+ if (child.IsChecked)
+ {
+ checkCount++;
+ }
+ }
+
+ itemLayout.IsChecked = false;
+
+ if (checkCount > 0)
+ {
+ itemLayout.IsChecked = true;
+ if (checkCount < folder.AppCount)
+ {
+ itemLayout.SetCheckOpacity(0.5);
+ }
+ else if (checkCount >= folder.AppCount)
+ {
+ itemLayout.SetCheckOpacity(1.0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public ItemLayout GetItemLayout(long id)
+ {
+ foreach (var item in AppsPage.Children)
+ {
+ if ((item.BindingContext as ItemInformation).Id == id)
+ {
+ return item as ItemLayout;
+ }
+ }
+
+ Log.Debug("Can not find item " + PageIndex + " : " + id);
+ return null;
+ }
+
+ public void RemoveItemLayout(ItemLayout item)
+ {
+ AppsPage.Children.Remove(item);
+ }
+
+ public void Clear()
+ {
+ ItemsSource.CollectionChanged -= ItemsSourceCollectionChanged;
+ }
+
+ public bool CheckEmpty()
+ {
+ return (AppsPage.Children.Count == 0);
+ }
+
+ private void MouseDown(object sender, EventArgs e)
+ {
+ Log.Debug("page - down");
+ ScrollViewMouseDown?.Invoke(sender, e);
+ }
+
+ private void MouseUp(object sender, EventArgs e)
+ {
+ Log.Debug("page - UP");
+ ScrollViewMouseUp?.Invoke(sender, e);
+ }
+
+ private void MouseMove(object sender, EventArgs e)
+ {
+ ScrollViewMouseMove?.Invoke(sender, e);
+ }
+
+ private void MouseHold(object sender, EventArgs e)
+ {
+ ScrollViewMouseHold?.Invoke(sender, e);
+
+ }
+
+ /// <summary>
+ /// A method will be called when any property in the view is changed.
+ /// </summary>
+ /// <param name="propertyName">A changed property's name</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == ItemsSourceProperty.PropertyName)
+ {
+ SetApp();
+ ItemsSource.CollectionChanged += ItemsSourceCollectionChanged;
+ }
+ else if (propertyName == UpdatedItemProperty.PropertyName)
+ {
+ foreach (var item in AppsPage.Children)
+ {
+ if (item is ItemLayout layout)
+ {
+ if ((layout.BindingContext as ItemInformation).Id == UpdatedItem)
+ {
+ layout.UpdateIcon();
+ UpdatedItem = -1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private void ItemsSourceCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ Log.Debug("Action for this event: " + e.Action);
+
+ /*
+ Add = 0,
+ Remove = 1,
+ Replace = 2,
+ Move = 3,
+ Reset = 4
+ */
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ var index = e.NewStartingIndex;
+
+ if (index >= PageIndex * MaxNumberOfApps && index < (PageIndex + 1) * MaxNumberOfApps)
+ {
+ foreach (var item in e.NewItems)
+ {
+ if (item is ItemInformation itemInfo)
+ {
+ MakeNewItem(index, itemInfo);
+ }
+
+ index++;
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ foreach (var item in e.OldItems)
+ {
+ if (item is ItemInformation itemInfo)
+ {
+ if (itemInfo.Index >= PageIndex * MaxNumberOfApps && itemInfo.Index < (PageIndex + 1) * MaxNumberOfApps)
+ {
+ foreach (var layout in AppsPage.Children)
+ {
+ if (layout.BindingContext == itemInfo)
+ {
+ if (AppsPage.Children.Contains(layout))
+ {
+ AppsPage.Children.Remove(layout);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ {
+ var index = e.OldStartingIndex;
+
+ foreach (var item in e.NewItems)
+ {
+
+ }
+
+ }
+
+ break;
+
+
+ case NotifyCollectionChangedAction.Move:
+ {
+ var index = e.NewStartingIndex;
+
+ if (index >= PageIndex * MaxNumberOfApps && index < (PageIndex + 1) * MaxNumberOfApps)
+ {
+ foreach (var newItem in e.NewItems)
+ {
+ ItemLayout item = PageScroller.GetItemLayout(index);
+ if (item == null)
+ {
+ break;
+ }
+
+ AppsPage.Children.Add(item, (index % MaxNumberOfApps) % 4, (index % MaxNumberOfApps) / 4);
+
+ if ((item.BindingContext as ItemInformation).Index / MaxNumberOfApps != index / MaxNumberOfApps)
+ {
+ PageScroller.RemoveItemLayout(item);
+ }
+
+ item.UpdateIcon();
+ break;
+ }
+ }
+
+ PageScroller.RemoveEmptyPage();
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ SetApp();
+ break;
+ }
+ }
+
+ public void Print()
+ {
+ foreach (var layout in AppsPage.Children)
+ {
+ if (layout is ItemLayout info)
+ {
+ Log.Debug($"Layout--{info.AppTitle.Text}");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.Apps.AppsPageScrollView"
+ xmlns:local="clr-namespace:Homescreen.View"
+ xmlns:apps="clr-namespace:Homescreen.View.Apps"
+ PageCount="{Binding PageCount, Mode=TwoWay}"
+ ScrollPosition="{Binding ScrollPosition, Mode=TwoWay}"
+ MoveFolderInsideCommand="{Binding MoveFolderInsideCommand, Mode=TwoWay}"
+ MoveFolderOutCommand="{Binding MoveFolderOutCommand, Mode=TwoWay}"
+ ScrollToCommand="{Binding ScrollToCommand}">
+
+ <ContentView.Content>
+ <AbsoluteLayout x:Name="AppsScrollViewBase">
+ <local:HomeScrollView
+ x:Name="AppsScrollView"
+ Orientation="Horizontal"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All">
+ <StackLayout
+ x:Name="AppsPageContainer"
+ Spacing="0"
+ Orientation="Horizontal">
+ </StackLayout>
+ </local:HomeScrollView>
+ <local:PageIndicator
+ x:Name="indicator"
+ AbsoluteLayout.LayoutBounds=".5, .985, .47, .052"
+ AbsoluteLayout.LayoutFlags="All"/>
+ </AbsoluteLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+using System.Collections.Specialized;
+using Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.ViewModel;
+using System.Collections.Generic;
+using System.Windows.Input;
+using Homescreen.View.Event;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// AppsPageScrollView manages displaying of AppsPages and make them scrolled horizontally as well.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AppsPageScrollView : ContentView
+ {
+ private AppsState state = AppsState.Normal;
+
+ private double EditModeGridX;
+ private double EditModeGridY;
+ private double EditModeUnitWidth;
+ private double EditModeUnitHeight;
+ private double offsetX;
+ private double offsetY;
+ private ItemThumbnail thumbnail = null;
+ private FolderInformation candidate = null;
+
+ /// <summary>
+ /// A command will be called if a folder is selected.
+ /// </summary>
+ public Command FolderOpenCommand { get; set; }
+
+ /// <summary>
+ /// An event handler if an app is selected.
+ /// </summary>
+ public EventHandler<ItemClickedEventArgs> OnAppClicked;
+
+ /// <summary>
+ /// An event handler if a scroll view is long pressed.
+ /// </summary>
+ public EventHandler OnScrollViewLongPressed;
+
+
+ /// <summary>
+ /// A property indicates current page index
+ /// </summary>
+ private int CurrentPageIndex => ((int)(ScrollPosition * PageCount + 0.5));
+
+ /// <summary>
+ /// An instance of HomeScrollView which is a main part of the AppsPageScrollView.
+ /// </summary>
+ public HomeScrollView PageScrollView
+ {
+ get => AppsScrollView;
+ }
+
+ /// <summary>
+ /// An instance of opened FolderLayout.
+ /// </summary>
+ public FolderLayout FolderView { get; set; }
+
+ #region Binding properties
+
+ /// <summary>
+ /// A bind-able property for ItemInformation
+ /// </summary>
+ public static readonly BindableProperty ItemsSourceProperty
+ = BindableProperty.Create("ItemsSource", typeof(ObservableCollection<ItemInformation>), typeof(AppsPageScrollView), default(ObservableCollection<ItemInformation>));
+
+ /// <summary>
+ /// An itemsSource is an app list which is binded with view model's app list.
+ /// </summary>
+ public ObservableCollection<ItemInformation> ItemsSource
+ {
+ get { return (ObservableCollection<ItemInformation>)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for PageCount.
+ /// </summary>
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(AppsPageScrollView), defaultValue: 0, defaultBindingMode: BindingMode.TwoWay);
+ /// <summary>
+ /// A PageCount is a number of the AppsPage is two way binded with view model.
+ /// </summary>
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ScrollPosition.
+ /// </summary>
+ public static readonly BindableProperty ScrollPositionProperty = BindableProperty.Create(
+ "ScrollPosition", typeof(double), typeof(AppsPageScrollView), defaultValue: 0.0, defaultBindingMode: BindingMode.TwoWay);
+
+ /// <summary>
+ /// A scroll position is two way binded with view model.
+ /// </summary>
+ public double ScrollPosition
+ {
+ get => (double)GetValue(ScrollPositionProperty);
+ set => SetValue(ScrollPositionProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ChoosedFolder.
+ /// </summary>
+ public static readonly BindableProperty ChoosedFolderProperty
+ = BindableProperty.Create("ChoosedFolder", typeof(FolderInformation),
+ typeof(FolderLayout), default(AppsPageScrollView));
+
+ /// <summary>
+ /// A selected folder will get selected apps by the Choose mode.
+ /// </summary>
+ public FolderInformation ChoosedFolder
+ {
+ get { return (FolderInformation)GetValue(ChoosedFolderProperty); }
+ set { SetValue(ChoosedFolderProperty, value); }
+ }
+
+
+ /// <summary>
+ /// BindableProperty to make binding with MoveFolderInsideCommand in AppsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty MoveFolderInsideCommandProperty
+ = BindableProperty.Create("MoveFolderInsideCommand", typeof(Command), typeof(AppsPageScrollView));
+
+ /// <summary>
+ /// An accessors for MoveFolderInsideCommand BindableProperty
+ /// </summary>
+ public Command MoveFolderInsideCommand
+ {
+ get => (Command)GetValue(MoveFolderInsideCommandProperty);
+ set => SetValue(MoveFolderInsideCommandProperty, value);
+ }
+
+ /// <summary>
+ /// BindableProperty to make binding with MoveFolderOutCommand in AppsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty MoveFolderOutCommandProperty
+ = BindableProperty.Create("MoveFolderOutCommand", typeof(Command), typeof(AppsPageScrollView));
+
+ /// <summary>
+ /// An accessors for MoveFolderOutCommand BindableProperty
+ /// </summary>
+ public Command MoveFolderOutCommand
+ {
+ get => (Command)GetValue(MoveFolderOutCommandProperty);
+ set => SetValue(MoveFolderOutCommandProperty, value);
+ }
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollToCommand in AppsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty ScrollToCommandProperty
+ = BindableProperty.Create("ScrollToCommand", typeof(Command), typeof(AppsPageScrollView), defaultBindingMode: BindingMode.OneWayToSource);
+
+ /// <summary>
+ /// An accessors for ScrollToCommand BindableProperty
+ /// </summary>
+ public Command ScrollToCommand
+ {
+ get => (Command)GetValue(ScrollToCommandProperty);
+ set => SetValue(ScrollToCommandProperty, value);
+ }
+
+
+ #endregion
+
+ public void Print()
+ {
+ Log.Debug("Print ItemSource");
+ foreach (var item in ItemsSource)
+ {
+ Log.Debug($"{item.Id}: {item.Index} :{item.Label}");
+ }
+
+ Log.Debug("Print Layout");
+ foreach (var page in AppsPageContainer.Children)
+ {
+ (page as AppsPageLayout).Print();
+ Log.Debug("----- Next Page -----");
+ }
+
+ Log.Debug("----- END -----");
+ }
+
+ /// <summary>
+ /// A constructor of AppsPageScrollView
+ /// </summary>
+ public AppsPageScrollView()
+ {
+ InitializeComponent();
+
+ AppsPageContainer.ChildAdded += (s, e) =>
+ {
+ PageCount = AppsPageContainer.Children.Count;
+ ScrollPosition = AppsScrollView.ScrollX / (AppsScrollView.Width * PageCount);
+ };
+
+ AppsPageContainer.ChildRemoved += (s, e) =>
+ {
+ PageCount = AppsPageContainer.Children.Count;
+ ScrollPosition = AppsScrollView.ScrollX / (AppsScrollView.Width * PageCount);
+ };
+
+ AppsScrollView.Scrolled += (s, e) =>
+ {
+ PageCount = AppsPageContainer.Children.Count;
+ ScrollPosition = AppsScrollView.ScrollX / (AppsScrollView.Width * PageCount);
+ };
+ }
+
+ private AppsPageLayout MakeNewPage()
+ {
+ var pageView = new AppsPageLayout
+ {
+ BindingContext = BindingContext,
+ PageIndex = AppsPageContainer.Children.Count,
+ PageScroller = this,
+ WidthRequest = DeviceInfo.Instance.Width,
+ ItemsSource = ItemsSource,
+ State = state,
+
+ ScrollViewMouseDown = ItemMouseDown,
+ ScrollViewMouseUp = ItemMouseUp,
+ ScrollViewMouseHold = ItemMouseHold,
+ ScrollViewMouseMove = ItemMouseMove,
+ };
+
+ pageView.OnItemClicked += ((sender, args) =>
+ {
+ if (args.Item is AppInformation)
+ {
+ OnAppClicked?.Invoke(sender, args);
+ }
+ else if (args.Item is FolderInformation)
+ {
+ FolderOpenCommand?.Execute(args.Item);
+ }
+ });
+ AppsPageContainer.Children.Add(pageView);
+ pageView.State = state;
+ return pageView;
+ }
+
+ private void SetApps()
+ {
+ AppsPageContainer.Children.Clear();
+
+ for (int i = 0; i < ItemsSource.Count; i++)
+ {
+ if (i % AppsPageLayout.MaxNumberOfApps == 0)
+ {
+ MakeNewPage();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method shows the indicator.
+ /// </summary>
+ public void ShowIndicator()
+ {
+ indicator.IsVisible = true;
+ }
+
+ /// <summary>
+ /// A method hides the indicator.
+ /// </summary>
+ public void HideIndicator()
+ {
+ indicator.IsVisible = false;
+ }
+
+ /// <summary>
+ /// A method updates icons in all AppsPages.
+ /// </summary>
+ public void UpdateIcon()
+ {
+ foreach (var page in AppsPageContainer.Children)
+ {
+ if (page is AppsPageLayout pageItem)
+ {
+ pageItem.UpdateIcon();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method updates check-box in all AppsPages.
+ /// </summary>
+ /// <param name="itemId">An item ID</param>
+ public void UpdateCheckBox(long itemId)
+ {
+ foreach (var page in AppsPageContainer.Children)
+ {
+ if (page is AppsPageLayout pageItem)
+ {
+ pageItem.UpdateCheckBox(itemId);
+ }
+ }
+ }
+
+ /// <summary>
+ /// A method provides An itemLayout matching position
+ /// </summary>
+ /// <param name="positionIndex">An item's position index</param>
+ /// <returns>A matching item layout</returns>
+ public ItemLayout GetItemLayout(int positionIndex)
+ {
+ ItemInformation item = null;
+ int i = 0;
+ foreach (ItemInformation itemInfo in ItemsSource)
+ {
+ if (i == positionIndex)
+ {
+ item = itemInfo;
+ break;
+ }
+
+ i++;
+ }
+
+ if (item == null)
+ {
+ Log.Debug("GetItemLayout : null");
+ return null;
+ }
+
+ AppsPageLayout page = AppsPageContainer.Children[item.Index / AppsPageLayout.MaxNumberOfApps] as AppsPageLayout;
+ return page?.GetItemLayout(item.Id);
+ }
+
+ /// <summary>
+ /// A method removes a matching ItemLayout.
+ /// </summary>
+ /// <param name="item">An item layout to be removed.</param>
+ public void RemoveItemLayout(ItemLayout item)
+ {
+ AppsPageLayout page = AppsPageContainer.Children[(item.BindingContext as ItemInformation).Index / AppsPageLayout.MaxNumberOfApps] as AppsPageLayout;
+ page.RemoveItemLayout(item);
+ }
+
+ /// <summary>
+ /// A methods empty pages.
+ /// </summary>
+ public void RemoveEmptyPage()
+ {
+ if (((ItemsSource.Count - 1) / AppsPageLayout.MaxNumberOfApps + 1 < AppsPageContainer.Children.Count) &&
+ (AppsPageContainer.Children[PageCount - 1] as AppsPageLayout).CheckEmpty())
+ {
+ AppsPageLayout page = AppsPageContainer.Children[PageCount - 1] as AppsPageLayout;
+ page.Clear();
+ AppsPageContainer.Children.RemoveAt(PageCount - 1);
+ }
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == "ItemsSource")
+ {
+ SetApps();
+ ItemsSource.CollectionChanged += ItemsSourceCollectionChanged;
+ ScrollToCommand = new Command<int>((index) =>
+ {
+ ScrollTo(index);
+ });
+ }
+ else if (propertyName == ChoosedFolderProperty.PropertyName)
+ {
+ Log.Debug("ChoosedFolder is updated, " + (ChoosedFolder == null ? "NULL" : ChoosedFolder.Label));
+
+ foreach (var layout in AppsPageContainer.Children)
+ {
+ if (layout is AppsPageLayout page)
+ {
+ page.UpdateChoosedFolder(ChoosedFolder);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets all items as the Edit state.
+ /// </summary>
+ public void SetEditState()
+ {
+ state = AppsState.Edit;
+ foreach (AppsPageLayout item in AppsPageContainer.Children)
+ {
+ item.State = AppsState.Edit;
+ }
+ }
+
+ /// <summary>
+ /// Sets all items as the Choose state.
+ /// </summary>
+ public void SetChooserState()
+ {
+ state = AppsState.Choose;
+ foreach (AppsPageLayout item in AppsPageContainer.Children)
+ {
+ item.State = AppsState.Choose;
+ }
+ }
+
+ /// <summary>
+ /// Sets all items as the Normal state.
+ /// </summary>
+ public void SetNormalState()
+ {
+ state = AppsState.Normal;
+ foreach (AppsPageLayout item in AppsPageContainer.Children)
+ {
+ item.State = AppsState.Normal;
+ }
+ }
+
+
+ public void ItemMouseDown(object sender, EventArgs e)
+ {
+ }
+
+ public void ItemMouseUp(object sender, EventArgs e)
+ {
+ if (sender is ItemLayout itemLayout)
+ {
+ if (thumbnail != null)
+ {
+ (thumbnail.Parent as AbsoluteLayout).Children.Remove(thumbnail);
+ thumbnail = null;
+ }
+
+ itemLayout.IsVisible = true;
+ AppsScrollView.HomeScrollViewScrollUnLockEventHandler?.Invoke(this, EventArgs.Empty);
+ if (candidate != null)
+ {
+ //Move to folder
+ if (itemLayout.BindingContext is AppInformation info)
+ {
+ info.ParentId = candidate.Id;
+ MoveFolderInsideCommand?.Execute(info);
+ }
+
+ candidate.IconPath = FolderInformation.FolderIconPath;
+ candidate.HaveToUpdate = true;
+
+ if (AppsPageContainer.Children[candidate.Index / AppsPageLayout.MaxNumberOfApps] is AppsPageLayout appsPageLayout)
+ {
+ appsPageLayout.UpdateIcon();
+ }
+
+ candidate = null;
+ }
+ else if (itemLayout.BindingContext is ItemInformation itemInformation &&
+ itemInformation.Index == -1)
+ {
+ MoveFolderOutCommand?.Execute(itemLayout.BindingContext as ItemInformation);
+ UpdateIcon();
+ }
+ }
+ }
+
+ public void ItemMouseMove(object sender, EventArgs e)
+ {
+ MouseEventArgs ev = e as MouseEventArgs;
+ if (sender is ItemLayout itemLayout && thumbnail != null)
+ {
+ AbsoluteLayout.SetLayoutBounds(thumbnail,
+ new Rectangle(ev.Current.X + offsetX, ev.Current.Y + offsetY, EditModeUnitWidth, EditModeUnitHeight));
+
+ if (itemLayout.BindingContext is AppInformation itemInfo)
+ {
+ if (itemInfo.ParentId != ItemInformation.InitialId && FolderView.IsVisible == false)
+ {
+ AppsScrollViewBase.Children.Add(thumbnail);
+ }
+ }
+
+ if (FolderView.IsVisible == true)
+ {
+ candidate = null;
+ return;
+ }
+
+ int centerX = (int)(ev.Current.X + offsetX + EditModeUnitWidth * 0.5);
+ int centerY = (int)(ev.Current.Y + offsetY + EditModeUnitHeight * 0.5);
+
+ if (centerY < EditModeGridY && centerY > EditModeGridY + EditModeUnitHeight * 4)
+ {
+ // Ignore
+ }
+ else if (centerX < EditModeGridX)
+ {
+ //move to left+
+ ScrollTo(ScrollPosition - 1, true);
+ }
+ else if (centerX > EditModeGridX + EditModeUnitWidth * 4)
+ {
+ // move to right
+ ScrollTo(ScrollPosition + 1, true);
+ }
+ else
+ {
+ centerX = (int)((centerX - EditModeGridX) / EditModeUnitWidth);
+ centerY = (int)((centerY - EditModeGridY) / EditModeUnitHeight);
+ int index = CurrentPageIndex * AppsPageLayout.MaxNumberOfApps + centerY * 4 + centerX;
+ if (index >= ItemsSource.Count)
+ {
+ Log.Debug("There is no item");
+ //Ignore.
+ }
+ else if (ItemsSource[index] is FolderInformation)
+ {
+ if (ItemsSource[index] != candidate)
+ {
+ if (candidate != null)
+ {
+ candidate.IconPath = FolderInformation.FolderIconPath;
+ candidate.HaveToUpdate = true;
+ (AppsPageContainer.Children[candidate.Index / AppsPageLayout.MaxNumberOfApps] as AppsPageLayout).UpdateIcon();
+ }
+
+ candidate = ItemsSource[index] as FolderInformation;
+ candidate.IconPath = FolderInformation.FolderSelectIconPath;
+ candidate.HaveToUpdate = true;
+ (AppsPageContainer.Children[CurrentPageIndex] as AppsPageLayout).UpdateIcon();
+ }
+
+ }
+ else if (candidate != null)
+ {
+ candidate.IconPath = FolderInformation.FolderIconPath;
+ candidate.HaveToUpdate = true;
+ (AppsPageContainer.Children[CurrentPageIndex] as AppsPageLayout).UpdateIcon();
+ candidate = null;
+ }
+ }
+ }
+
+ }
+
+ public void ItemMouseHold(object sender, EventArgs e)
+ {
+ MouseEventArgs ev = e as MouseEventArgs;
+ if (sender is ItemLayout itemLayout &&
+ itemLayout.BindingContext is ItemInformation itemInformation)
+ {
+ double startX = 0;
+ double startY = 0;
+ candidate = null;
+
+ EditModeUnitWidth = Width * 0.9 * 0.25;
+ EditModeUnitHeight = Height * 0.9 * 0.85 * 0.2;
+ EditModeGridY = (Height * 0.05) +
+ (((Height * 0.9) - (Height * 0.85 * 0.9)) * 0.4);
+ EditModeGridX = Width * 0.05;
+
+ if (AppsPageContainer.Children[0].Scale == 1 ||
+ ((itemLayout.BindingContext is AppInformation appInformation) && appInformation.ParentId != ItemInformation.InitialId))
+ {
+ startX = itemLayout.Geometry.X - (int)(itemInformation.Index / AppsPageLayout.MaxNumberOfApps) * Width;
+ startY = itemLayout.Geometry.Y;
+ }
+ else
+ {
+ startX = ((itemInformation.Index - CurrentPageIndex * AppsPageLayout.MaxNumberOfApps) % 4) * EditModeUnitWidth + EditModeGridX;
+ startY = (int)((itemInformation.Index - CurrentPageIndex * AppsPageLayout.MaxNumberOfApps) / 4) * EditModeUnitHeight + EditModeGridY;
+ }
+
+ offsetX = startX - ev.Current.X;
+ offsetY = startY - ev.Current.Y;
+ itemLayout.IsVisible = false;
+
+ thumbnail = new ItemThumbnail()
+ {
+ ThumbnailInfo = (itemLayout.BindingContext as ItemInformation)
+ };
+
+ if (((itemLayout.BindingContext is AppInformation) && (itemLayout.BindingContext as AppInformation).ParentId != ItemInformation.InitialId))
+ {
+ FolderView.FolderLayoutBase.Children.Add(thumbnail);
+ }
+ else
+ {
+ AppsScrollViewBase.Children.Add(thumbnail);
+ }
+
+ AbsoluteLayout.SetLayoutBounds(thumbnail,
+ new Rectangle(startX, startY, EditModeUnitWidth, EditModeUnitHeight));
+ thumbnail.UpdateIcon();
+ AppsScrollView.HomeScrollViewScrollLockEventHandler?.Invoke(this, EventArgs.Empty);
+ }
+
+ if (AppsPageContainer.Children[0].Scale == 1)
+ {
+ OnScrollViewLongPressed?.Invoke(this, EventArgs.Empty);
+ }
+
+ }
+
+ private void ScrollTo(double index, bool animation = true)
+ {
+ if (index < 0)
+ {
+ index = 0;
+ }
+ else if (index >= PageCount)
+ {
+ index = PageCount - 1;
+ }
+
+ AppsScrollView.ScrollToAsync(index * AppsScrollView.Width, 0, animation);
+ }
+
+ private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ //Log.Debug($"AppsPageScrollView event: {e.Action}");
+
+ /*
+ Add = 0,
+ Remove = 1,
+ Replace = 2,
+ Move = 3,
+ Reset = 4
+ */
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ int index = e.NewStartingIndex;
+ Log.Debug($"ItemsSourceCollectionChanged :: ADD");
+ foreach (var item in e.NewItems)
+ {
+ if (index % AppsPageLayout.MaxNumberOfApps == 0)
+ {
+ AppsPageLayout page = MakeNewPage();
+ }
+
+ index++;
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ SetApps();
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Apps.BadgeLayout"
+ IsVisible="False">
+
+ <local:NinePatch
+ x:Name="background"
+ Source="icon_badge_background.png"
+ BorderLeft="25"
+ BorderRight="25"
+ BorderBottom="0"
+ BorderTop="0"
+ Aspect="Fill"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=TextBlock, Property=Width, Factor=1}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=TextBlock, Property=Height, Factor=1}"/>
+
+ <StackLayout
+ x:Name="TextBlock">
+ <Label
+ TextColor="White"
+ FontSize="Medium"
+ x:Name="count"
+ HorizontalTextAlignment="End"/>
+ </StackLayout>
+</RelativeLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.ComponentModel;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+using Homescreen.Debug;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// BadgeLayout displays a badge, usually this is used in the ItemLayout.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class BadgeLayout : RelativeLayout
+ {
+ private static readonly string MaxBadgeCount = "999+";
+
+ private long badgeCount;
+ /// <summary>
+ /// A number of Badge
+ /// </summary>
+ public long BadgeCount
+ {
+ get
+ {
+ return badgeCount;
+ }
+
+ set
+ {
+ badgeCount = value;
+
+ if (value == 0)
+ {
+ IsVisible = false;
+ }
+ else
+ {
+ IsVisible = true;
+
+ if (value > 999)
+ {
+ count.Text = MaxBadgeCount;
+ }
+ else
+ {
+ count.Text = "" + badgeCount;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// A constructor of BadgeLayout which initializes width and height of widget display region.
+ /// </summary>
+ public BadgeLayout()
+ {
+ InitializeComponent();
+
+ double side = DeviceInfo.Instance.Width * 0.0208;
+ double upDown = DeviceInfo.Instance.Height * 0.0039;
+ TextBlock.Padding = new Thickness(side, upDown, side, upDown);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.Apps.CheckBox"
+ InputTransparent="True">
+
+ <Image
+ x:Name="Background"
+ Source="color_check_white_bg.png"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+ <Image
+ x:Name="OffStroke"
+ Source="color_check_stroke.png"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+ <Image
+ x:Name="CheckIcon"
+ Source="core_check_icon.png"
+ IsVisible="False"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+</RelativeLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// CheckBox view class which is used to display a check box on the ItemLayout.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class CheckBox : RelativeLayout
+ {
+ /// <summary>
+ /// A bind-able property for IsChecked
+ /// </summary>
+ public static readonly BindableProperty IsCheckedProperty
+ = BindableProperty.Create("IsChecked", typeof(bool), typeof(CheckBox), default(bool));
+
+ /// <summary>
+ /// IsChecked property indicated whether this check box is checked or not.
+ /// </summary>
+ public bool IsChecked
+ {
+ get => (bool)GetValue(IsCheckedProperty);
+ set
+ {
+ SetValue(IsCheckedProperty, value);
+ if (value)
+ {
+ CheckIcon.IsVisible = true;
+ OffStroke.Source = "color_check_bg.png";
+ }
+ else
+ {
+ CheckIcon.IsVisible = false;
+ OffStroke.Source = "color_check_stroke.png";
+ }
+ }
+ }
+
+ /// <summary>
+ /// a Check Image
+ /// </summary>
+ public Image CheckImage { get => CheckIcon; }
+
+ /// <summary>
+ /// a constructor of CheckBox
+ /// </summary>
+ public CheckBox()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.View.Event;
+using Homescreen.View.Interface;
+using Homescreen.ViewModel;
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// Choose State which makes Apps page to select apps for a folder.
+ /// </summary>
+ public class ChooseState : IAppsState
+ {
+ private EventHandler<ItemClickedEventArgs> OnAppClicked;
+
+ /// <summary>
+ /// An interface of Apps page's layouts.
+ /// </summary>
+ public IAppsLayout StateLayout { get; private set; }
+
+ /// <summary>
+ /// An interface of the state handler.
+ /// </summary>
+ public IAppsStateHandler StateHandler { get; private set; }
+
+ /// <summary>
+ /// Apps page's current state
+ /// </summary>
+ public AppsState State => AppsState.Choose;
+
+ /// <summary>
+ /// A constructor of ChooseState which sets interfaces related.
+ /// </summary>
+ /// <param name="appsHandler">A state handler</param>
+ /// <param name="appsLayout">An Apps layout interface</param>
+ public ChooseState(IAppsStateHandler appsHandler, IAppsLayout appsLayout)
+ {
+ StateHandler = appsHandler;
+ StateLayout = appsLayout;
+ }
+
+ /// <summary>
+ /// A method will be called while new state is starting.
+ /// </summary>
+ public void StepIn()
+ {
+ Log.Debug("Apps: Choose StepIn ");
+
+ DeviceInfo.Instance.StatusBarMode = StatusBarMode.Transparent;
+
+ StateLayout.MenuButton.IsVisible = false;
+ StateLayout.WidgetsButton.IsVisible = false;
+ StateLayout.ChooserBar.IsVisible = true;
+
+ StateLayout.ChooserBar.DoneChoosing = new Command(() =>
+ {
+ StateHandler.SetState(StateHandler.GetPreviousState());
+ StateLayout.FolderView.IsVisible = true;
+ StateLayout.PageScroller.UpdateIcon();
+ StateLayout.FolderView.SetFolderInformation();
+ });
+
+ StateLayout.ChooserBar.CancelChoosing = new Command(() =>
+ {
+ StateHandler.SetState(StateHandler.GetPreviousState());
+ });
+
+ OnAppClicked = new EventHandler<ItemClickedEventArgs>((sender, args) =>
+ {
+ if (args.Item is AppInformation appInformation)
+ {
+ Log.Debug("[Choose] ItemClicked");
+ Log.Debug($"folder app count = {StateLayout.PageScroller.ChoosedFolder.AppCount}, " +
+ $"choosed count = {StateLayout.ChooserBar.ChoosedApps.Count}");
+
+ if (appInformation.IsChecked == false &&
+ StateLayout.PageScroller.ChoosedFolder.AppCount + StateLayout.ChooserBar.ChoosedApps.Count >= 9)
+ {
+ DependencyService.Get<IToastPopup>().Display("Maximum number of applications in folder (9) reached");
+ return;
+ }
+
+ appInformation.IsChecked = !appInformation.IsChecked;
+ args.Layout.IsChecked = appInformation.IsChecked;
+ if (appInformation.ParentId != ItemInformation.InitialId)
+ {
+ StateLayout.PageScroller.UpdateCheckBox(appInformation.ParentId);
+ }
+
+ StateLayout.MainLayout.ChooseAppCommand.Execute(appInformation);
+ }
+ });
+
+ StateLayout.FolderView.OnAppClicked += OnAppClicked;
+ StateLayout.PageScroller.OnAppClicked += OnAppClicked;
+
+ StateLayout.PageScroller.SetChooserState();
+ StateLayout.FolderView.IsVisible = false;
+ }
+
+ /// <summary>
+ /// A method will be called while state is replaced by another new state.
+ /// </summary>
+ public void StepOut()
+ {
+ StateLayout.FolderView.OnAppClicked -= OnAppClicked;
+ StateLayout.PageScroller.OnAppClicked -= OnAppClicked;
+
+ StateLayout.ChooserBar.DoneChoosing = null;
+ StateLayout.ChooserBar.CancelChoosing = null;
+
+ DeviceInfo.Instance.StatusBarMode = StatusBarMode.Translucent;
+ }
+
+ /// <summary>
+ /// A method will be called when the back key is pressed.
+ /// </summary>
+ public void OnBack()
+ {
+ Log.Debug("Choose OnBack");
+ if (StateLayout.FolderView.IsVisible)
+ {
+ StateLayout.FolderView.IsVisible = false;
+ return;
+ }
+
+ StateLayout.ChooserBar.ChooseCancelCommand.Execute(string.Empty);
+ StateHandler.SetState(StateHandler.GetPreviousState());
+ }
+
+ /// <summary>
+ /// A method will be called when the home key is pressed.
+ /// </summary>
+ public void OnHome()
+ {
+
+ }
+
+ /// <summary>
+ /// A method will be called when the menu key is pressed.
+ /// </summary>
+ public void OnMenu()
+ {
+
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.Apps.ChooserTopBar"
+ ChooseDoneCommand="{Binding ChooseDoneCommand}"
+ ChooseCancelCommand="{Binding ChooseCancelCommand}">
+
+ <ContentView.Content>
+ <Grid Padding="0" Margin="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="24*" />
+ <ColumnDefinition Width="52*" />
+ <ColumnDefinition Width="24*" />
+ </Grid.ColumnDefinitions>
+
+ <Label x:Name="CancelButton"
+ FontSize="Medium"
+ TextColor="White"
+ VerticalTextAlignment="Center"
+ HorizontalTextAlignment="Center"
+ Text="CANCEL"
+ Grid.Column="0"/>
+
+ <Label x:Name="ChooseStatus"
+ FontSize="Large"
+ TextColor="White"
+ VerticalTextAlignment="Center"
+ HorizontalTextAlignment="Center"
+ Text="0 selected"
+ Grid.Column="1"/>
+
+ <Label x:Name="DoneButton"
+ FontSize="Medium"
+ TextColor="White"
+ VerticalTextAlignment="Center"
+ HorizontalTextAlignment="Center"
+ Text="DONE"
+ Grid.Column="2"/>
+ </Grid>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// ChooserTopBar class which displays Done, Cancel buttons and
+ /// a number of selected apps in Choose mode.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class ChooserTopBar : ContentView
+ {
+ /// <summary>
+ /// A command will be executed when app selecting is canceled.
+ /// </summary>
+ public ICommand CancelChoosing;
+
+ /// <summary>
+ /// A command will be executed when app selecting is finished.
+ /// </summary>
+ public ICommand DoneChoosing;
+
+ #region Binding Properties
+
+ /// <summary>
+ /// A bind-able property for ChooseDoneCommand
+ /// </summary>
+ public static readonly BindableProperty ChooseDoneCommandProperty
+ = BindableProperty.Create("ChooseDoneCommand", typeof(Command), typeof(ChooserTopBar));
+ /// <summary>
+ /// A ChooseDoneCommand which will be used when the app selecting is finished.
+ /// </summary>
+ public Command ChooseDoneCommand
+ {
+ get => (Command)GetValue(ChooseDoneCommandProperty);
+ set => SetValue(ChooseDoneCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ChooseCancelcommand.
+ /// </summary>
+ public static readonly BindableProperty ChooseCancelCommandProperty
+ = BindableProperty.Create("ChooseCancelCommand", typeof(Command), typeof(ChooserTopBar));
+ /// <summary>
+ /// A ChooseCancelCommand which will be used when the app selecting is canceled.
+ /// </summary>
+ public Command ChooseCancelCommand
+ {
+ get => (Command)GetValue(ChooseCancelCommandProperty);
+ set => SetValue(ChooseCancelCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ChoosedApps.
+ /// </summary>
+ public static readonly BindableProperty ChoosedAppsProperty
+ = BindableProperty.Create("ChoosedApps", typeof(ICollection<ItemInformation>),
+ typeof(AppsLayout), default(ICollection<ItemInformation>));
+
+ /// <summary>
+ /// A ChoosedApps which will have selected apps during Choose mode.
+ /// </summary>
+ public ICollection<ItemInformation> ChoosedApps
+ {
+ get { return (ICollection<ItemInformation>)GetValue(ChoosedAppsProperty); }
+ set { SetValue(ChoosedAppsProperty, value); }
+ }
+ #endregion
+
+ /// <summary>
+ /// A constructor of ChooserTopBar initializes buttons TapGestureRecognizers for selecting done and cancel.
+ /// </summary>
+ public ChooserTopBar()
+ {
+ InitializeComponent();
+
+ TapGestureRecognizer cancelTapGesture = new TapGestureRecognizer();
+ cancelTapGesture.Tapped += (s, e) =>
+ {
+ Log.Debug($"Cancel button clicked : {CancelChoosing} :");
+ ChooseCancelCommand?.Execute(String.Empty);
+ CancelChoosing?.Execute(String.Empty);
+ };
+ CancelButton.GestureRecognizers.Add(cancelTapGesture);
+
+ TapGestureRecognizer doneTapGesture = new TapGestureRecognizer();
+ doneTapGesture.Tapped += (s, e) =>
+ {
+ Log.Debug($"Done button clicked : {DoneChoosing} :");
+ ChooseDoneCommand?.Execute(String.Empty);
+ DoneChoosing?.Execute(String.Empty);
+ };
+ DoneButton.GestureRecognizers.Add(doneTapGesture);
+ }
+
+ /// <summary>
+ /// A method will be called when any property in the view is changed
+ /// </summary>
+ /// <param name="propertyName">A changed property's name</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == ChoosedAppsProperty.PropertyName)
+ {
+ ChooseStatus.Text = $"{ChoosedApps.Count} selected";
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Interface;
+using Homescreen.ViewModel;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// Drag State which makes Apps page to be prepared for the app dragging.
+ /// </summary>
+ public class DragState : IAppsState
+ {
+ /// <summary>
+ /// An interface of Apps page's layouts.
+ /// </summary>
+ public IAppsLayout StateLayout { get; private set; }
+
+ /// <summary>
+ /// An interface of the state handler.
+ /// </summary>
+ public IAppsStateHandler StateHandler { get; private set; }
+
+ /// <summary>
+ /// Apps page's current state
+ /// </summary>
+ public AppsState State => AppsState.Drag;
+
+ /// <summary>
+ /// A constructor of DragState which sets interfaces related.
+ /// </summary>
+ /// <param name="appsHandler">A state handler</param>
+ /// <param name="appsLayout">An Apps layout interface</param>
+ public DragState(IAppsStateHandler appsHandler, IAppsLayout appsLayout)
+ {
+ StateHandler = appsHandler;
+ StateLayout = appsLayout;
+ }
+
+ /// <summary>
+ /// A method will be called while new state is starting.
+ /// </summary>
+ public void StepIn()
+ {
+ Debug.Log.Debug("Apps:DragState StepIn ");
+
+ StateLayout.MenuButton.IsVisible = false;
+ StateLayout.WidgetsButton.IsVisible = false;
+ }
+
+ /// <summary>
+ /// A method will be called while state is replaced by another new state.
+ /// </summary>
+ public void StepOut()
+ {
+ }
+
+ /// <summary>
+ /// A method will be called when the back key is pressed.
+ /// </summary>
+ public void OnBack()
+ {
+ StateHandler.SetState(StateHandler.GetEditState());
+ }
+
+ /// <summary>
+ /// A method will be called when the home key is pressed.
+ /// </summary>
+ public void OnHome()
+ {
+
+ }
+
+ /// <summary>
+ /// A method will be called when the menu key is pressed.
+ /// </summary>
+ public void OnMenu()
+ {
+
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Interface;
+using Homescreen.ViewModel;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// Edit State which makes Apps page to remove apps or folders.
+ /// </summary>
+ public class EditState : IAppsState
+ {
+ /// <summary>
+ /// An interface of Apps page's layouts.
+ /// </summary>
+ public IAppsLayout StateLayout { get; private set; }
+
+ /// <summary>
+ /// An interface of the state handler.
+ /// </summary>
+ public IAppsStateHandler StateHandler { get; private set; }
+
+ /// <summary>
+ /// Apps page's current state
+ /// </summary>
+ public AppsState State => AppsState.Edit;
+
+ /// <summary>
+ /// A constructor of EditState which sets interfaces related.
+ /// </summary>
+ /// <param name="appsHandler">A state handler</param>
+ /// <param name="appsLayout">An Apps layout interface</param>
+ public EditState(IAppsStateHandler appsHandler, IAppsLayout appsLayout)
+ {
+ StateHandler = appsHandler;
+ StateLayout = appsLayout;
+ }
+
+ /// <summary>
+ /// A method will be called while new state is starting.
+ /// </summary>
+ public void StepIn()
+ {
+ Debug.Log.Debug("Edit StepIn ");
+ StateLayout.MenuButton.IsVisible = false;
+ StateLayout.WidgetsButton.IsVisible = false;
+ StateLayout.ChooserBar.IsVisible = false;
+
+ StateLayout.PageScroller.SetEditState();
+ }
+
+ /// <summary>
+ /// A method will be called while state is replaced by new another state.
+ /// </summary>
+ public void StepOut()
+ {
+
+ }
+
+ /// <summary>
+ /// A method will be called when the back key is pressed.
+ /// </summary>
+ public void OnBack()
+ {
+ Debug.Log.Debug("Edit OnBack");
+
+ if (StateLayout.FolderView.IsVisible == true)
+ {
+ StateLayout.FolderView.IsVisible = false;
+ return;
+ }
+
+ StateHandler.SetState(StateHandler.GetNormalState());
+ }
+
+ public void OnHome()
+ {
+
+ }
+
+ /// <summary>
+ /// A method will be called when the menu key is pressed.
+ /// </summary>
+ public void OnMenu()
+ {
+
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Apps.FolderLayout"
+ UpdatedItem="{Binding UpdatedItemId}"
+ >
+ <ContentView.Content>
+ <AbsoluteLayout x:Name="folderLayoutBase">
+ <BoxView
+ x:Name="Background"
+ BackgroundColor="Black"
+ Opacity="0.39"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"/>
+ <BoxView x:Name="FolderWhiteBg"
+ BackgroundColor="White"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.92, 0.6"/>
+ <AbsoluteLayout
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.92, 0.6">
+ <local:EntryView
+ x:Name="FolderName"
+ Placeholder="Unnamed folder"
+ PlaceholderColor="#60606060"
+ HorizontalTextAlignment="Center"
+ FontSize="32"
+ TextColor="#4DE7FF"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.02, 0.84, 0.1"/>
+ <BoxView
+ BackgroundColor="#4de7ff"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.123, 0.84, 0.005" />
+ <Grid
+ x:Name="FolderGrid"
+ ColumnSpacing="10"
+ RowSpacing="10"
+ Padding="10"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.7, 0.84, 0.81" >
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ </Grid>
+ </AbsoluteLayout>
+ </AbsoluteLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.ViewModel;
+using System;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+using Homescreen.View.Event;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// FolderLayout which displays title and apps of a selected folder.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class FolderLayout : ContentView
+ {
+ /// <summary>
+ /// A FolderLabel property to provides selected folder's name.
+ /// </summary>
+ public Entry FolderLabel
+ {
+ get => FolderName;
+ }
+
+ /// <summary>
+ /// An event handler for App clicked situation.
+ /// </summary>
+ public EventHandler<ItemClickedEventArgs> OnAppClicked;
+
+ private AppsState appState;
+ /// <summary>
+ /// An Apps state property
+ /// </summary>
+ public AppsState AppsState
+ {
+ get => appState;
+ set
+ {
+ appState = value;
+
+ foreach (var inApp in FolderGrid.Children)
+ {
+ if (inApp is ItemLayout itemLayout)
+ {
+ itemLayout.State = appState;
+ }
+ }
+ }
+ }
+
+ public EventHandler ScrollviewMouseDown { get; set; }
+ public EventHandler ScrollviewMouseUp { get; set; }
+ public EventHandler ScrollviewMouseHold { get; set; }
+ public EventHandler ScrollviewMouseMove { get; set; }
+
+ public AbsoluteLayout FolderLayoutBase { get => folderLayoutBase; }
+
+
+ #region Biding Properties
+
+ /// <summary>
+ /// A bind-able property for ClickedCommand
+ /// </summary>
+ public static readonly BindableProperty ClickedCommandProperty
+ = BindableProperty.Create("ClickedCommand", typeof(Command<ItemInformation>), typeof(FolderLayout));
+ /// <summary>
+ /// A ClickedCommand property which will be called when the item(app, folder) is clicked)
+ /// </summary>
+ public Command<ItemInformation> ClickedCommand
+ {
+ get => (Command<ItemInformation>)GetValue(ClickedCommandProperty);
+ set => SetValue(ClickedCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for FolderTextChangedCommand
+ /// </summary>
+ public static readonly BindableProperty FolderTextChangedCommandProperty
+ = BindableProperty.Create("FolderTextChangedCommand", typeof(Command), typeof(FolderLayout));
+ /// <summary>
+ /// A FolderTextChangedCommand property will be called if folder's text is changed.
+ /// </summary>
+ public Command FolderTextChangedCommand
+ {
+ get => (Command)GetValue(FolderTextChangedCommandProperty);
+ set => SetValue(FolderTextChangedCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for LaunchAppCommand
+ /// </summary>
+ public static readonly BindableProperty LaunchAppCommandProperty
+ = BindableProperty.Create("LaunchAppCommand", typeof(Command), typeof(FolderLayout));
+ /// <summary>
+ /// A LaunchAppCommand which is called if an app is clicked.
+ /// </summary>
+ public Command LaunchAppCommand
+ {
+ get => (Command)GetValue(LaunchAppCommandProperty);
+ set => SetValue(LaunchAppCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for ChooseStartCommand
+ /// </summary>
+ public static readonly BindableProperty ChooseStartCommandProperty
+ = BindableProperty.Create("ChooseStartCommand", typeof(Command<FolderInformation>), typeof(FolderLayout));
+ /// <summary>
+ /// A ChooseStartCommand which is called if Choose mode should have to started.
+ /// </summary>
+ public Command<FolderInformation> ChooseStartCommand
+ {
+ get => (Command<FolderInformation>)GetValue(ChooseStartCommandProperty);
+ set => SetValue(ChooseStartCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A bind-able property for OpenedFolder
+ /// </summary>
+ public static readonly BindableProperty OpenedFolderProperty
+ = BindableProperty.Create("OpenedFolder", typeof(FolderInformation),
+ typeof(FolderLayout), default(FolderInformation), defaultBindingMode: BindingMode.TwoWay);
+ /// <summary>
+ /// An OpenedFolder property which will be called if a folder is clicked.
+ /// </summary>
+ public FolderInformation OpenedFolder
+ {
+ get { return (FolderInformation)GetValue(OpenedFolderProperty); }
+ set { SetValue(OpenedFolderProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for UpdatedItem.
+ /// </summary>
+ public static readonly BindableProperty UpdatedItemProperty
+ = BindableProperty.Create("UpdatedItem", typeof(long), typeof(FolderLayout), default(long));
+
+ /// <summary>
+ /// An UpdatedItem property which will be used for UI update with adding DB id of updating apps.
+ /// </summary>
+ public long UpdatedItem
+ {
+ get { return (long)GetValue(UpdatedItemProperty); }
+ set { SetValue(UpdatedItemProperty, value); }
+ }
+
+ /// <summary>
+ /// A bind-able property for ChoosedApps.
+ /// </summary>
+ public static readonly BindableProperty ChoosedAppsProperty
+ = BindableProperty.Create("ChoosedApps", typeof(ICollection<ItemInformation>),
+ typeof(FolderLayout), default(ICollection<ItemInformation>));
+
+ /// <summary>
+ /// A ChoosedApps which will have selected apps during Choose mode.
+ /// </summary>
+ public ICollection<ItemInformation> ChoosedApps
+ {
+ get { return (ICollection<ItemInformation>)GetValue(ChoosedAppsProperty); }
+ set { SetValue(ChoosedAppsProperty, value); }
+ }
+ #endregion
+
+
+
+ /// <summary>
+ /// A Constructor of the FolderLayout.
+ /// </summary>
+ public FolderLayout()
+ {
+ InitializeComponent();
+ TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer
+ {
+ Command = new Command(OnBgTapped),
+ CommandParameter = Background
+ };
+ Background.GestureRecognizers.Add(tapGestureRecognizer);
+
+ FolderName.Completed += (s, e) =>
+ {
+ Log.Debug($"Text changed completed {FolderName.Text}");
+ FolderTextChangedCommand?.Execute(FolderName.Text);
+ };
+ }
+
+ /// <summary>
+ /// A method updates title and apps by a selected folder information.
+ /// </summary>
+ public void SetFolderInformation()
+ {
+ FolderLabel.Text = OpenedFolder.Label;
+ SetAppList(OpenedFolder.AppList);
+ }
+
+ private void SetAppList(IEnumerable<AppInformation> appList)
+ {
+ FolderGrid.Children.Clear();
+ int count = 0;
+ int i = 0;
+
+ foreach (var appInfo in appList)
+ {
+ count += 1;
+ ItemLayout itemLayout = new ItemLayout();
+ itemLayout.AppTitle.TextColor = Color.Black;
+ itemLayout.Icon.PressedColor = Color.LightGray;
+ itemLayout.BindingContext = appInfo;
+ itemLayout.State = AppsState;
+ if (ChoosedApps.Contains(appInfo))
+ {
+ itemLayout.IsChecked = true;
+ }
+
+ itemLayout.PageMouseDown = FolderViewMouseDown;
+ itemLayout.PageMouseUp = FolderViewMouseUp;
+ itemLayout.PageMouseHold = FolderViewMouseHold;
+ itemLayout.PageMouseMove = FolderViewMouseMove;
+
+ itemLayout.OnItemClicked += ((sender, args) =>
+ {
+ OnAppClicked?.Invoke(sender, args);
+ });
+ FolderGrid.Children.Add(itemLayout, i % 3, i / 3);
+ i++;
+ }
+
+ Log.Debug($"applist.count : {count}, Current State : {AppsState}");
+
+ if (count < 9 &&
+ AppsState != AppsState.Choose)
+ {
+ AppInformation appInfo = new AppInformation()
+ {
+ Label = "",
+ IconPath = "btn_add_nor.png",
+ BadgeCount = 0,
+ AppId = "",
+ PackageId = "",
+ IsRemovable = false,
+ };
+ ItemLayout itemLayout = new ItemLayout();
+ itemLayout.Icon.PressedColor = Color.White;
+ itemLayout.Icon.PressedSource = "btn_add_press.png";
+ itemLayout.BindingContext = appInfo;
+ itemLayout.State = AppsState.Normal;
+ itemLayout.OnItemClicked += ((sender, args) =>
+ {
+ Log.Debug("+ button Clicked in Folder");
+ ChooseStartCommand?.Execute(OpenedFolder);
+ });
+ FolderGrid.Children.Add(itemLayout, i % 3, i / 3);
+ }
+ }
+
+ private void OnBgTapped(object parameter)
+ {
+ IsVisible = false;
+ Log.Debug("Folder outside tabbed");
+ FolderTextChangedCommand?.Execute(FolderName.Text);
+ }
+
+ private void FolderViewMouseDown(object sender, EventArgs arg)
+ {
+ ScrollviewMouseDown?.Invoke(sender, arg);
+ }
+
+ private void FolderViewMouseUp(object sender, EventArgs arg)
+ {
+ (sender as ItemLayout).IsVisible = true;
+ ScrollviewMouseUp?.Invoke(sender, arg);
+ }
+
+ private void FolderViewMouseHold(object sender, EventArgs arg)
+ {
+ (sender as ItemLayout).IsVisible = false;
+ ScrollviewMouseHold?.Invoke(sender, arg);
+ }
+
+ private void FolderViewMouseMove(object sender, EventArgs arg)
+ {
+ MouseEventArgs ev = arg as MouseEventArgs;
+ if ((sender as ItemLayout).IsVisible == false)
+ {
+ if (ev.Current.X < FolderWhiteBg.X || ev.Current.X > FolderWhiteBg.X + FolderWhiteBg.Width ||
+ ev.Current.Y < FolderWhiteBg.Y || ev.Current.Y > FolderWhiteBg.Y + FolderWhiteBg.Height)
+ {
+ IsVisible = false;
+ OpenedFolder.RemoveApp(((sender as ItemLayout).BindingContext as ItemInformation).Id);
+ OpenedFolder.HaveToUpdate = true;
+ ((sender as ItemLayout).BindingContext as ItemInformation).Index = -1;
+ }
+
+ ScrollviewMouseMove?.Invoke(sender, arg);
+ }
+ }
+
+ /// <summary>
+ /// A method will be called when any property in the view is changed
+ /// </summary>
+ /// <param name="propertyName">A changed property's name</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == OpenedFolderProperty.PropertyName)
+ {
+ Log.Debug("OpenedFolder is updated, " + OpenedFolder.Label);
+ SetFolderInformation();
+ }
+ else if (propertyName == UpdatedItemProperty.PropertyName)
+ {
+ if (OpenedFolder != null && UpdatedItem == OpenedFolder.Id)
+ {
+ SetAppList(OpenedFolder.AppList);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ xmlns:localApps="clr-namespace:Homescreen.View.Apps"
+ x:Class="Homescreen.View.Apps.ItemLayout">
+
+ <ContentView.Content>
+ <AbsoluteLayout
+ x:Name="ItemMainLayout">
+
+ <local:ClickableImage
+ x:Name="icon"
+ NormalColor="White"
+ PressedColor="LightGray"
+ Source="{Binding IconPath}"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.05, 0.70, 0.58"/>
+
+ <Grid InputTransparent="True"
+ x:Name="iconGrid"
+ IsVisible="False"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.2, 0.45, 0.35">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ </Grid>
+
+ <Label Text="{Binding Label}"
+ x:Name="Title"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0, 1, 1, 0.35"
+ TextColor="White"
+ LineBreakMode="CharacterWrap"
+ VerticalTextAlignment="Start"
+ HorizontalTextAlignment="Center"/>
+
+ </AbsoluteLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.ViewModel;
+using System;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.View.Event;
+using System.Collections.Generic;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// ItemLayout which displays an app or a folder based on binding ItemInformation.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class ItemLayout : ContentView
+ {
+ private bool isClickable = true;
+
+ private BadgeLayout badgeCountLayout;
+ private BadgeLayout BadgeCountLayout
+ {
+ get
+ {
+ if (badgeCountLayout == null)
+ {
+ MakeBadgeLayout();
+ }
+
+ return badgeCountLayout;
+ }
+ }
+
+ private CheckBox checkBoxLayout = null;
+ private CheckBox CheckBoxLayout
+ {
+ get
+ {
+ if (checkBoxLayout == null)
+ {
+ MakeCheckBoxLayout();
+ }
+
+ return checkBoxLayout;
+ }
+ }
+
+ private ImageButton deleteButton = null;
+ private ImageButton DeleteButton
+ {
+ get
+ {
+ if (deleteButton == null)
+ {
+ MakeDeleteButton();
+ }
+
+ return deleteButton;
+ }
+ }
+
+ private AppsState state = AppsState.Normal;
+
+ /// <summary>
+ /// Apps page current state
+ /// </summary>
+ public AppsState State
+ {
+ get => state;
+ set
+ {
+ if (state == value)
+ {
+ return;
+ }
+
+ state = value;
+
+ switch (state)
+ {
+ case AppsState.Normal:
+ HideDeleteButton();
+ HideCheckBox();
+ break;
+ case AppsState.Edit:
+ ShowDeleteButton();
+ HideCheckBox();
+ break;
+ case AppsState.Choose:
+ IsChecked = false;
+ CheckBoxLayout.CheckImage.Opacity = 1.0;
+ ShowCheckBox();
+ break;
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// App Geometry
+ /// </summary>
+ public Rectangle Geometry
+ {
+ get
+ {
+ VisualElement parent = (VisualElement)Parent;
+ if (parent == null)
+ {
+ return Bounds;
+ }
+
+ double geometryX = X;
+ double geometryY = Y;
+ while (!(parent is AppsPageScrollView) && !(parent is FolderLayout))
+ {
+ if (parent == null)
+ {
+ break;
+ }
+
+ geometryY += parent.Y;
+ geometryX += parent.X;
+ parent = (VisualElement)parent.Parent;
+ };
+
+ return new Rectangle { X = geometryX, Y = geometryY, Width = Width, Height = Height };
+ }
+ }
+
+
+ /// <summary>
+ /// An event handler which will be called if this item is clicked.
+ /// </summary>
+ public EventHandler<ItemClickedEventArgs> OnItemClicked;
+
+ /// <summary>
+ /// A command which will be called if this item's delete button is clicked in Edit mode.
+ /// </summary>
+ public Command DeleteItemCommand { get; set; }
+
+ /// <summary>
+ /// An icon represents the binding ItemInformation
+ /// </summary>
+ public ClickableImage Icon { get => icon; }
+
+ /// <summary>
+ /// A grid for displaying Apps of a folder.
+ /// </summary>
+ public Grid IconGrid { get => iconGrid; }
+
+ /// <summary>
+ /// A flag indicates whether this item is selected or not.
+ /// </summary>
+ public bool IsChecked
+ {
+ get => CheckBoxLayout.IsChecked;
+ set
+ {
+ CheckBoxLayout.IsChecked = value;
+ ItemInformation itemInfo = this.BindingContext as ItemInformation;
+ itemInfo.IsChecked = value;
+ }
+
+ }
+
+ /// <summary>
+ /// An item title.
+ /// </summary>
+ public Label AppTitle
+ {
+ get => Title;
+ }
+
+ public EventHandler PageMouseDown { get; set; }
+ public EventHandler PageMouseUp { get; set; }
+ public EventHandler PageMouseMove { get; set; }
+ public EventHandler PageMouseHold { get; set; }
+
+
+ EventHandler MouseClick { get; set; }
+
+ /// <summary>
+ /// A constructor of the ItemLayout.
+ /// </summary>
+ public ItemLayout()
+ {
+ InitializeComponent();
+
+ icon.Clicked += ItemClicked;
+ icon.Pressed += ItemPressed;
+ icon.Released += ItemReleased;
+ icon.Hold += MouseTouchHold;
+ icon.Move += MouseTouchMove;
+ }
+
+ private void MakeBadgeLayout()
+ {
+ if (badgeCountLayout != null)
+ {
+ return;
+ }
+
+ badgeCountLayout = new BadgeLayout();
+ badgeCountLayout.BindingContext = BindingContext;
+ AbsoluteLayout.SetLayoutBounds(badgeCountLayout, new Rectangle(.85, .005, .28, .28));
+ AbsoluteLayout.SetLayoutFlags(badgeCountLayout, AbsoluteLayoutFlags.All);
+ ItemMainLayout.Children.Add(badgeCountLayout);
+ }
+
+
+ private void MakeCheckBoxLayout()
+ {
+ if (checkBoxLayout != null)
+ {
+ return;
+ }
+
+ Log.Debug("MakeCheckBoxLayout make");
+ checkBoxLayout = new CheckBox()
+ {
+ HorizontalOptions = LayoutOptions.EndAndExpand,
+ };
+ AbsoluteLayout.SetLayoutBounds(checkBoxLayout, new Rectangle(.85, .005, .28, .28));
+ AbsoluteLayout.SetLayoutFlags(checkBoxLayout, AbsoluteLayoutFlags.All);
+ ItemMainLayout.Children.Add(checkBoxLayout);
+ }
+
+ private void MakeDeleteButton()
+ {
+ Log.Debug("MakeDeleteButton");
+ if (deleteButton != null)
+ {
+ return;
+ }
+
+ Log.Debug("MakeDeleteButton delete");
+ deleteButton = new ImageButton()
+ {
+ HorizontalOptions = LayoutOptions.Start,
+ VerticalOptions = LayoutOptions.Start,
+ NormalImage = "btn_delete_nor.png",
+ PressedImage = "btn_delete_press.png",
+ };
+ AbsoluteLayout.SetLayoutBounds(deleteButton, new Rectangle(0, 0, .35, .3));
+ AbsoluteLayout.SetLayoutFlags(deleteButton, AbsoluteLayoutFlags.All);
+
+ deleteButton.Clicked += DeleteButtonClicked;
+
+ ItemMainLayout.Children.Add(deleteButton);
+ }
+
+ private void ShowDeleteButton()
+ {
+ ItemInformation itemInfo = this.BindingContext as ItemInformation;
+
+ if (itemInfo.IsRemovable)
+ {
+ DeleteButton.IsVisible = true;
+ }
+
+ }
+
+ private void HideDeleteButton()
+ {
+ if (deleteButton == null)
+ {
+ return;
+ }
+
+ DeleteButton.IsVisible = false;
+ }
+
+ private void ShowCheckBox()
+ {
+ CheckBoxLayout.IsVisible = true;
+ }
+
+ private void HideCheckBox()
+ {
+ if (checkBoxLayout == null)
+ {
+ return;
+ }
+
+ CheckBoxLayout.IsVisible = false;
+ }
+
+ /// <summary>
+ /// A method which set opacity of check image
+ /// </summary>
+ /// <param name="opacity">opacity of check image</param>
+ public void SetCheckOpacity(double opacity)
+ {
+ CheckBoxLayout.CheckImage.Opacity = opacity;
+ }
+
+ /// <summary>
+ /// A method which set this item disabled for selecting.
+ /// </summary>
+ public void DisableClick()
+ {
+ Opacity = 0.5;
+ isClickable = false;
+ CheckBoxLayout.IsVisible = false;
+ }
+
+ /// <summary>
+ /// A method which set this item enabled for selecting.
+ /// </summary>
+ public void EnableClick()
+ {
+ Opacity = 1;
+ Icon.NormalColor = Color.White;
+ isClickable = true;
+ if (State == AppsState.Choose)
+ {
+ CheckBoxLayout.IsVisible = true;
+ }
+ }
+
+ /// <summary>
+ /// A method which update the icon by following binding context.
+ /// </summary>
+ public void UpdateIcon()
+ {
+ if (BindingContext is FolderInformation folderInfo)
+ {
+ Log.Debug("UpdateIcon called : " + folderInfo.Id);
+ int i = 0;
+ IconGrid.IsVisible = true;
+
+ IconGrid.Children.Clear();
+ foreach (AppInformation appItem in folderInfo.AppList)
+ {
+ Log.Debug("UpdateIcon called : " + appItem.Label);
+ Image icon = new Image
+ {
+ Source = appItem.IconPath,
+ Aspect = Aspect.Fill,
+ };
+ IconGrid.Children.Add(icon, i % 2, i / 2);
+ i++;
+ if (i == 4)
+ {
+ break;
+ }
+ }
+ }
+
+ if (BindingContext is ItemInformation item)
+ {
+ Icon.Source = item.IconPath;
+ Title.Text = item.Label;
+ if (item.BadgeCount > 0 ||
+ (item.BadgeCount == 0 && badgeCountLayout != null))
+ {
+ BadgeCountLayout.BadgeCount = item.BadgeCount;
+
+ if (item.BadgeCount > 999)
+ {
+ AbsoluteLayout.SetLayoutBounds(BadgeCountLayout, new Rectangle(1, .005, .58, .28));
+ AbsoluteLayout.SetLayoutFlags(BadgeCountLayout, AbsoluteLayoutFlags.All);
+ }
+ else if (item.BadgeCount > 99)
+ {
+ AbsoluteLayout.SetLayoutBounds(BadgeCountLayout, new Rectangle(1, .005, .48, .28));
+ AbsoluteLayout.SetLayoutFlags(BadgeCountLayout, AbsoluteLayoutFlags.All);
+ }
+ else if (item.BadgeCount > 9)
+ {
+ AbsoluteLayout.SetLayoutBounds(BadgeCountLayout, new Rectangle(1, .005, .38, .28));
+ AbsoluteLayout.SetLayoutFlags(BadgeCountLayout, AbsoluteLayoutFlags.All);
+ }
+ }
+ }
+ }
+
+
+ private void ItemClicked(object sender, EventArgs e)
+ {
+ if (isClickable == false)
+ {
+ return;
+ }
+
+ ItemInformation itemInfo = this.BindingContext as ItemInformation;
+ Log.Debug($"ItemClicked : {itemInfo.Label}[{sender}], isFolder?{itemInfo.GetDataObject().IsFolder}");
+
+ OnItemClicked?.Invoke(sender, new ItemClickedEventArgs()
+ {
+ Layout = this,
+ Item = itemInfo,
+ });
+ }
+
+ private void ItemPressed(object sender, EventArgs e)
+ {
+ Log.Debug("ItemPressed");
+ if (isClickable == false)
+ {
+ return;
+ }
+
+ iconGrid.Opacity = 0.5;
+ PageMouseDown?.Invoke(this, e);
+ }
+
+ private void ItemReleased(object sender, EventArgs e)
+ {
+ Log.Debug("ItemReleased");
+ if (isClickable == false)
+ {
+ return;
+ }
+
+ iconGrid.Opacity = 1;
+ PageMouseUp?.Invoke(this, e);
+ }
+
+ private void MouseTouchHold(Object sender, EventArgs e)
+ {
+ Log.Debug("MouseTouchHold");
+ PageMouseHold?.Invoke(this, e);
+ }
+
+ private void MouseTouchMove(Object sender, EventArgs e)
+ {
+ PageMouseMove?.Invoke(this, e);
+ }
+
+ private void DeleteButtonClicked(object sender, EventArgs e)
+ {
+ ItemInformation itemInfo = this.BindingContext as ItemInformation;
+
+ string title = "Uninstall";
+ string description = itemInfo.Label + " will be uninstalled.";
+ string leftButtonTitle = "Uninstall";
+
+ if (itemInfo is FolderInformation folderInfo)
+ {
+ List<AppInformation> list = folderInfo.AppList as List<AppInformation>;
+ if (list.Count == 0)
+ {
+ DeleteItemCommand?.Execute(itemInfo);
+ return;
+ }
+
+ title = "Remove Folder";
+ description = "Folder will be removed. Applications in this folder will not be uninstalled.";
+ leftButtonTitle = "Remove";
+ }
+
+ var dialog = new TwoButtonPopup()
+ {
+ Title = title,
+ Content = new Label()
+ {
+ FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
+ Margin = new Thickness(DeviceInfo.Instance.Width * 0.04, 0, DeviceInfo.Instance.Width * 0.04, 0),
+ LineBreakMode = LineBreakMode.WordWrap,
+ HeightRequest = 100D / 1280D * DeviceInfo.Instance.Height,
+ },
+ };
+ if (dialog.Content is Label label)
+ {
+ label.Text = description;
+ }
+
+ var leftButton = new Button() { Text = leftButtonTitle };
+ leftButton.Clicked += (s, arg) =>
+ {
+ DeleteItemCommand?.Execute(itemInfo);
+ dialog.Hide();
+ };
+ dialog.FirstButton = leftButton;
+
+ var rightButton = new Button() { Text = "Cancel" };
+ rightButton.Clicked += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+ dialog.SecondButton = rightButton;
+
+ dialog.Show();
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ if (BindingContext is ItemInformation item)
+ {
+ if (item.BadgeCount > 0)
+ {
+ BadgeCountLayout.BadgeCount = item.BadgeCount;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ xmlns:localApps="clr-namespace:Homescreen.View.Apps"
+ x:Class="Homescreen.View.Apps.ItemThumbnail">
+ <local:ClickableImage
+ x:Name="thumbnailIcon"
+ NormalColor="LightGray"
+ PressedColor="LightGray"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.05, 0.70, 0.58"/>
+ <Grid InputTransparent="True"
+ x:Name="ThumbnailIconGrid"
+ IsVisible="False"
+ AbsoluteLayout.LayoutFlags="All"
+ AbsoluteLayout.LayoutBounds="0.5, 0.2, 0.45, 0.35">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ </Grid>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using Homescreen.DataModel;
+using Homescreen.Debug;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// ItemThumbnail which displays an app or a folder based on binding ItemInformation.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class ItemThumbnail : AbsoluteLayout
+ {
+ public ItemInformation ThumbnailInfo { get; set; }
+ public ItemThumbnail()
+ {
+ InitializeComponent();
+ Log.Debug("SetColor");
+ }
+
+ public void UpdateIcon()
+ {
+ thumbnailIcon.Source = ThumbnailInfo.IconPath;
+ if (ThumbnailInfo is FolderInformation folderInfo)
+ {
+ Log.Debug("UpdateIcon called : " + folderInfo.Id);
+ int i = 0;
+ ThumbnailIconGrid.IsVisible = true;
+
+ ThumbnailIconGrid.Children.Clear();
+ foreach (AppInformation appItem in folderInfo.AppList)
+ {
+ Log.Debug("UpdateIcon called : " + appItem.Label);
+ Image icon = new Image
+ {
+ Source = appItem.IconPath,
+ };
+ ThumbnailIconGrid.Children.Add(icon, i % 2, i / 2);
+ i++;
+ if (i == 4)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.View.Event;
+using Homescreen.View.Interface;
+using Homescreen.ViewModel;
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.View.Apps
+{
+ /// <summary>
+ /// Normal State which makes Apps page to select apps for a folder.
+ /// </summary>
+ public class NormalState : IAppsState
+ {
+ private OptionMenuPopup popup;
+ private EventHandler<ItemClickedEventArgs> OnAppClicked;
+
+ private EventHandler OnScrollViewLongPressed;
+
+ /// <summary>
+ /// An interface of Apps page's layouts.
+ /// </summary>
+ public IAppsLayout StateLayout { get; private set; }
+
+ /// <summary>
+ /// An interface of the state handler.
+ /// </summary>
+ public IAppsStateHandler StateHandler { get; private set; }
+
+ /// <summary>
+ /// Apps page's current state
+ /// </summary>
+ public AppsState State => AppsState.Normal;
+
+ /// <summary>
+ /// A constructor of ChooseState which sets interfaces related.
+ /// </summary>
+ /// <param name="appsHandler">A state handler</param>
+ /// <param name="appsLayout">An Apps layout interface</param>
+ public NormalState(IAppsStateHandler appsHandler, IAppsLayout appsLayout)
+ {
+ StateHandler = appsHandler;
+ StateLayout = appsLayout;
+ }
+
+ /// <summary>
+ /// A method will be called while new state is starting.
+ /// </summary>
+ public void StepIn()
+ {
+ Log.Debug("Apps:Normal StepIn ");
+
+ StateLayout.MenuButton.OnClickedCommand = new Command(() =>
+ {
+ popup = new OptionMenuPopup();
+ popup.AddMenuItem("Edit", () =>
+ {
+ Log.Debug("Edit is selected");
+
+ StateHandler.SetState(StateHandler.GetEditState());
+ });
+
+ popup.AddMenuItem("Create folder", () =>
+ {
+ Log.Debug("Create folder is selected");
+ HomeMessagingCenter.Send(this, Message.CreateFolder);
+ });
+
+ popup.Show(StateLayout.MenuButton);
+ });
+
+ StateLayout.WidgetsButton.OnClickedCommand = new Command(() =>
+ {
+ HomeMessagingCenter.Send(this, Message.ShowWidgets);
+ });
+
+ StateLayout.MenuButton.IsVisible = true;
+ StateLayout.WidgetsButton.IsVisible = true;
+ StateLayout.ChooserBar.IsVisible = false;
+
+ OnAppClicked = new EventHandler<ItemClickedEventArgs>((sender, args) =>
+ {
+ if (args.Item is AppInformation appInformation)
+ {
+ Log.Debug("[Normal] ItemClicked, Normal");
+ StateLayout.MainLayout.LaunchAppCommand.Execute(args.Item);
+ }
+ });
+
+ StateLayout.FolderView.OnAppClicked += OnAppClicked;
+ StateLayout.PageScroller.OnAppClicked += OnAppClicked;
+
+ OnScrollViewLongPressed = new EventHandler((sender, args) =>
+ {
+ StateHandler.SetState(StateHandler.GetEditState());
+ });
+
+ StateLayout.PageScroller.OnScrollViewLongPressed += OnScrollViewLongPressed;
+
+ StateLayout.PageScroller.SetNormalState();
+ }
+
+ /// <summary>
+ /// A method will be called while state is replaced by another new state.
+ /// </summary>
+ public void StepOut()
+ {
+ popup?.Hide();
+ popup = null;
+
+ StateLayout.MenuButton.OnClickedCommand = null;
+ StateLayout.WidgetsButton.OnClickedCommand = null;
+
+ StateLayout.FolderView.OnAppClicked -= OnAppClicked;
+ StateLayout.PageScroller.OnAppClicked -= OnAppClicked;
+
+ StateLayout.PageScroller.OnScrollViewLongPressed -= OnScrollViewLongPressed;
+ }
+
+ /// <summary>
+ /// A method will be called when the back key is pressed.
+ /// </summary>
+ public void OnBack()
+ {
+ Log.Debug("Apps:Normal OnBack ");
+
+ if (StateLayout.FolderView.IsVisible == true)
+ {
+ StateLayout.FolderView.IsVisible = false;
+ return;
+ }
+
+ HomeMessagingCenter.Send(this, Message.ShowWidgets);
+ }
+
+ /// <summary>
+ /// A method will be called when the home key is pressed.
+ /// </summary>
+ public void OnHome()
+ {
+ HomeMessagingCenter.Send(this, Message.ShowWidgets);
+ }
+
+ /// <summary>
+ /// A method will be called when the menu key is pressed.
+ /// </summary>
+ public void OnMenu()
+ {
+ if (popup == null ||
+ popup.IsDismissed)
+ {
+ StateLayout.MenuButton.OnClickedCommand.Execute(string.Empty);
+ }
+ else
+ {
+ popup.Hide();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for ClickableImage inherited from Image and implements IMouseEventReceiver
+ /// </summary>
+ public class ClickableImage : Image, IMouseEventReceiver
+ {
+ /// <summary>
+ /// Property for normal color
+ /// </summary>
+ public Color NormalColor { get; set; } = Color.White;
+ /// <summary>
+ /// Property for pressed color
+ /// </summary>
+ public Color PressedColor { get; set; } = Color.White;
+ /// <summary>
+ /// Property for image source on pressed
+ /// </summary>
+ public ImageSource PressedSource { get; set; }
+
+ /// <summary>
+ /// EventHandler for MouseDown event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseDown { get; set; }
+ /// <summary>
+ /// EventHandler for MouseUp event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseUp { get; set; }
+ /// <summary>
+ /// EventHandler for MouseMove event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseMove { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseHold event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseHold { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseClick event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseClick { get; private set; }
+
+ /// <summary>
+ /// EventHandler for clicked event
+ /// </summary>
+ public event EventHandler Clicked;
+ /// <summary>
+ /// EventHandler for pressed event
+ /// </summary>
+ public event EventHandler Pressed;
+ /// <summary>
+ /// EventHandler for released event
+ /// </summary>
+ public event EventHandler Released;
+ public event EventHandler Hold;
+ public event EventHandler Move;
+
+ private ImageSource normalSource;
+
+ /// <summary>
+ /// Constructor for ClickableImage
+ /// Adds method that are called when mouse events are occurred
+ /// </summary>
+ public ClickableImage()
+ {
+ MouseDown += (s, e) => OnPressed();
+ MouseUp += (s, e) => OnReleased();
+ MouseClick += (s, e) => OnClick();
+ MouseHold += (s, e) => OnHold(s, e);
+ MouseMove += (s, e) => OnMove(s, e);
+ }
+
+ /// <summary>
+ /// This method will be call when mouse is clicked
+ /// </summary>
+ public void OnClick()
+ {
+ Log.Debug("OnClick");
+ if (normalSource != null)
+ {
+ Source = normalSource;
+ }
+
+ Clicked?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// This method will be call when mouse is pressed
+ /// </summary>
+ public void OnPressed()
+ {
+ Log.Debug("OnPressed");
+ if (PressedSource != null)
+ {
+ normalSource = Source;
+ Source = PressedSource;
+ }
+
+ Pressed?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// This method will be call when mouse is released
+ /// </summary>
+ public void OnReleased()
+ {
+ Log.Debug("OnReleased");
+ if (normalSource != null)
+ {
+ Source = normalSource;
+ }
+
+ Released?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void OnHold(object sender, EventArgs e)
+ {
+ Hold?.Invoke(this, e);
+ }
+
+ public void OnMove(object sender, EventArgs e)
+ {
+ Move?.Invoke(this, e);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// EntryView class for CustomRendered Entry
+ /// </summary>
+ public class EntryView : Entry
+ {
+
+ }
+}
--- /dev/null
+using Homescreen.DataModel;
+using Homescreen.View.Apps;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.View.Event
+{
+ public class ItemClickedEventArgs : EventArgs
+ {
+ public ItemLayout Layout { get; set; }
+ public ItemInformation Item { get; set; }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Homescreen.View.Apps;
+
+namespace Homescreen.View.Event
+{
+ class ItemLayoutMouseEvent : EventArgs
+ {
+ public ItemLayout Layout { get; set; }
+ public int X { set; get; }
+ public int Y { set; get; }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.View.Event
+{
+ public class WidgetLongPressEvent : EventArgs
+ {
+ public long LongPressedWidgetId { set; get; }
+ }
+}
--- /dev/null
+using System;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Event
+{
+ class WidgetPageEvent : EventArgs
+ {
+ public WidgetPageInformation PageInfo { set; get; }
+ public int X { set; get; }
+ public int Y { set; get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for HomeScrollView inherited from ScrollView and implements IMouseEventReceiver
+ /// </summary>
+ public class HomeScrollView : ScrollView, IMouseEventReceiver
+ {
+ /// <summary>
+ /// EventHandler to deal with behavior regarding Scroller provided from Tizen
+ /// </summary>
+ public EventHandler HomeScrollViewScrollLockEventHandler;
+ /// <summary>
+ /// EventHandler to deal with behavior regarding Scroller provided from Tizen
+ /// </summary>
+ public EventHandler HomeScrollViewScrollUnLockEventHandler;
+
+ /// <summary>
+ /// EventHandler for MouseDown event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseDown { get; }
+ /// <summary>
+ /// EventHandler for MouseUp event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseUp { get; }
+ /// <summary>
+ /// EventHandler for MouseMove event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseMove { get; }
+ /// <summary>
+ /// EventHandler for MouseHold event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseHold { get; protected set; }
+ /// <summary>
+ /// EventHandler for MouseClick event
+ /// MouseUp property must be declared, because WidgetPageScrollView implement IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseClick { get; }
+
+ /// <summary>
+ /// EventHandler to deal with MouseHold event
+ /// </summary>
+ public event EventHandler LongPressed;
+
+ public HomeScrollView()
+ {
+ MouseHold += (s, e) => LongPressed?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ xmlns:apps="clr-namespace:Homescreen.View.Apps"
+ x:Class="Homescreen.View.HomescreenMainPage">
+ <ContentPage.Content>
+ <RelativeLayout
+ Margin="0"
+ Padding="0"
+ HorizontalOptions="CenterAndExpand"
+ VerticalOptions="CenterAndExpand">
+
+ <view:Wallpaper
+ x:Name="wallpaper"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"/>
+
+ <widgets:WidgetsLayout
+ x:Name="widgets"
+ IsVisible="False"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+
+ <apps:AppsLayout
+ x:Name="apps"
+ Opacity="0.0"
+ IsVisible="False"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+
+ </RelativeLayout>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.View.Interface;
+using Homescreen.ViewModel;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for HomscreenMainPage that is main page of this application
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class HomescreenMainPage : ContentPage, IBackHomeSignalReceiver
+ {
+ /// <summary>
+ /// Property for WallPaper
+ /// </summary>
+ public Wallpaper WallPaper { get => wallpaper; }
+ /// <summary>
+ /// Property for Widgets
+ /// </summary>
+ public IVisibilityChanged Widgets { get => widgets; }
+ /// <summary>
+ /// Property for Apps
+ /// </summary>
+ public IVisibilityChanged Apps { get => apps; }
+ /// <summary>
+ /// Property for current IVisibilityChanged object
+ /// </summary>
+ public IVisibilityChanged Current { get; set; }
+
+ /// <summary>
+ /// Constructor for HomescreenMainPage
+ /// </summary>
+ public HomescreenMainPage()
+ {
+ Log.Debug("---Initialize HomescreenMainPage---");
+
+ InitializeComponent();
+
+ Current = widgets;
+
+ HomeMessagingCenter.Subscribe(this, Message.ShowWidgets, (sender) => ShowWidgets());
+ HomeMessagingCenter.Subscribe(this, Message.ShowApps, (sender) => ShowApps());
+ }
+
+ /// <summary>
+ /// This method will be called when this main page is shown
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ /* There seems to be a timing issue between the mouse down event point,
+ * the AppsLayout drawing point, and the Button animation point.
+ * Apps must be displayed before they are hidden and should not be hidden before OnAppearing.
+ * Otherwise, the Touch event does not occur in Apps. */
+ apps.IsVisible = true;
+
+ apps.IsVisible = false;
+ widgets.IsVisible = true;
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed
+ /// </summary>
+ /// <returns>True</returns>
+ protected override bool OnBackButtonPressed()
+ {
+ Log.Debug("OnBackKeyPressed");
+
+ Current?.OnBackKeyPressed();
+
+ /* If true is returned, the back key is blocked here. */
+ return true;
+ }
+
+ /// <summary>
+ /// This method will be called when this page received "ShowWidgets" message
+ /// </summary>
+ private void ShowWidgets()
+ {
+ if (Current != Widgets)
+ {
+ Current = Widgets;
+ Widgets.Show();
+ Apps.Hide();
+ Log.Debug("ShowWidgets");
+ }
+ }
+
+ /// <summary>
+ /// This method will be called when this page received "ShowApps" message
+ /// </summary>
+ private void ShowApps()
+ {
+ if (Current != Apps)
+ {
+ Current = Apps;
+ Apps.Show();
+ Widgets.Hide();
+ Log.Debug("ShowApps");
+ }
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed
+ /// </summary>
+ public void OnBackKeyPressed()
+ {
+ Log.Debug("OnBackKeyPressed");
+
+ Current?.OnBackKeyPressed();
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed
+ /// </summary>
+ public void OnHomeKeyPressed()
+ {
+ Log.Debug("OnHomeKeyPressed");
+
+ Current?.OnHomeKeyPressed();
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed
+ /// </summary>
+ public void OnMenuKeyPressed()
+ {
+ Log.Debug("OnMenuKeyPressed");
+
+ Current?.OnMenuKeyPressed();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for ImageButton inherited from AbsoluteLayout
+ /// </summary>
+ public class ImageButton : AbsoluteLayout
+ {
+ /// <summary>
+ /// Property for normal image source
+ /// </summary>
+ public ImageSource NormalImage { get => normalImage.Source; set => normalImage.Source = value; }
+
+ /// <summary>
+ /// Property for pressed image source
+ /// </summary>
+ public ImageSource PressedImage { get => pressedImage.Source; set => pressedImage.Source = value; }
+
+ private ClickableImage normalImage;
+ private Image pressedImage;
+
+ /// <summary>
+ /// EventHandler to deal with click event
+ /// </summary>
+ public event EventHandler Clicked;
+
+ public ClickableImage ClickableImage { get => normalImage; }
+
+ /// <summary>
+ /// Constructor for ImageButton
+ /// </summary>
+ public ImageButton()
+ {
+ normalImage = new ClickableImage { Aspect = Aspect.Fill };
+ pressedImage = new Image { Aspect = Aspect.Fill };
+
+ Children.Add(pressedImage);
+ Children.Add(normalImage);
+
+ normalImage.Pressed += (sender, e) =>
+ {
+ normalImage.FadeTo(0, 300, Easing.Linear);
+ pressedImage.FadeTo(1, 300, Easing.Linear);
+ };
+ normalImage.Released += (sender, e) =>
+ {
+ normalImage.FadeTo(1, 300, Easing.Linear);
+ pressedImage.FadeTo(0, 300, Easing.Linear);
+ };
+
+ normalImage.Clicked += (sender, e) =>
+ {
+ Log.Debug("ImageButton Clicked");
+ Clicked?.Invoke(sender, e);
+ };
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<Grid xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.IndicatorUnit">
+ <local:ClickableImage x:Name="unitImage"/>
+ <Label x:Name="centerLabel"
+ VerticalOptions="Center"
+ HorizontalOptions="Center"
+ FontSize="Small"
+ Text="1"
+ IsVisible="False"
+ InputTransparent="True"/>
+</Grid>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for IndicatorUnit
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class IndicatorUnit : Grid
+ {
+ /// <summary>
+ /// Property to represents center or not
+ /// </summary>
+ private bool isCenter;
+ /// <summary>
+ /// An accessors for isCenter property
+ /// </summary>
+ public bool IsCenter
+ {
+ get => isCenter;
+ set
+ {
+ isCenter = value;
+
+ unitImage.Source = isCenter ? "page_indicator_center.png" : "page_indicator_unit.png";
+ unitImage.Aspect = isCenter ? Aspect.AspectFit : Aspect.Fill;
+ centerLabel.IsVisible = isCenter;
+ }
+ }
+
+ /// <summary>
+ /// Property for rotation which indicate how many angle should be changed
+ /// </summary>
+ private double rotation;
+ /// <summary>
+ /// An accessors for rotation property
+ /// </summary>
+ new public double Rotation
+ {
+ get => IsCenter ? rotation : base.Rotation;
+ set
+ {
+ if (IsCenter)
+ {
+ rotation = value;
+ }
+ else
+ {
+ base.Rotation = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Property for Text that represents the index of current page
+ /// </summary>
+ public string Text
+ {
+ get => centerLabel.Text;
+ set => centerLabel.Text = value;
+ }
+
+ /*
+ * I removed 'event' for the indicator UI Test.
+ * Use outside of IndicatorUnit only to the left of '- =', '+ ='.
+ */
+ public EventHandler Clicked;
+
+ public int PageIndex { get; set; }
+
+ /// <summary>
+ /// Constructor for IndicatorUnit
+ /// </summary>
+ /// <param name="pageIndex">Index of the page that indicator unit should represent</param>
+ public IndicatorUnit(int pageIndex)
+ {
+ PageIndex = pageIndex;
+
+ InitializeComponent();
+
+ unitImage.Source = "page_indicator_unit.png";
+ unitImage.Aspect = Aspect.Fill;
+
+ unitImage.Clicked += (s, e) =>
+ {
+ Clicked?.Invoke(this, EventArgs.Empty);
+ };
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// An interface for displaying an alert pop-up.
+ /// </summary>
+ public interface IAlertPopup
+ {
+ /// <summary>
+ /// A pop-up title
+ /// </summary>
+ string Title { get; set; }
+
+ /// <summary>
+ /// A pop-up content view which will be displayed in a pop-up
+ /// </summary>
+ Xamarin.Forms.View Content { get; set; }
+
+ /// <summary>
+ /// First button among three pop-up buttons.
+ /// </summary>
+ Button FirstButton { get; set; }
+ /// <summary>
+ /// Second button among three pop-up buttons.
+ /// </summary>
+ Button SecondButton { get; set; }
+ /// <summary>
+ /// Third button among three pop-up buttons.
+ /// </summary>
+ Button ThirdButton { get; set; }
+
+ /// <summary>
+ /// Show a pop-up.
+ /// </summary>
+ void Show();
+
+ /// <summary>
+ /// Hide a pop-up.
+ /// </summary>
+ void Hide();
+ }
+
+ /// <summary>
+ /// OneButtonPopup provides a pop-up with just one button.
+ /// This pop-up is normal used for giving information to an user.
+ /// </summary>
+ public class OneButtonPopup
+ {
+
+ /// <summary>
+ /// A pop-up title
+ /// </summary>
+ public string Title
+ {
+ get => popup.Title;
+ set => popup.Title = value;
+ }
+
+ /// <summary>
+ /// A pop-up content view which will be displayed in a pop-up
+ /// </summary>
+ public Xamarin.Forms.View Content
+ {
+ get => popup.Content;
+ set => popup.Content = value;
+ }
+
+ /// <summary>
+ /// A button pop-up inside
+ /// </summary>
+ public Button FirstButton
+ {
+ get => popup.FirstButton;
+ set => popup.FirstButton = value;
+ }
+
+ /// <summary>
+ /// A pop-up instance
+ /// </summary>
+ protected IAlertPopup popup;
+
+ /// <summary>
+ /// A constructor to make a one button pop-up
+ /// </summary>
+ public OneButtonPopup()
+ {
+ popup = DependencyService.Get<IAlertPopup>();
+ }
+
+ /// <summary>
+ /// Show a pop-up.
+ /// </summary>
+ public void Hide()
+ {
+ popup.Hide();
+ }
+
+ /// <summary>
+ /// Hide a pop-up.
+ /// </summary>
+ public void Show()
+ {
+ popup.Show();
+ }
+ }
+
+ /// <summary>
+ /// TwoButtonPopup provides a pop-up with yes, no buttons.
+ /// This pop-up is normal used to get user's choice.
+ /// </summary>
+ public class TwoButtonPopup : OneButtonPopup
+ {
+ /// <summary>
+ /// A Second button in the TwoButtonPopup.
+ /// </summary>
+ public Button SecondButton
+ {
+ get => popup.SecondButton;
+ set => popup.SecondButton = value;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Apps;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Xamarin.Forms;
+
+namespace Homescreen.View.Interface
+{
+ /// <summary>
+ /// An interface providing the AppsLayout's inner layouts.
+ /// </summary>
+ public interface IAppsLayout
+ {
+ /// <summary>
+ /// The Apps main layout
+ /// </summary>
+ AppsLayout MainLayout { get; }
+
+ /// <summary>
+ /// The Menu button which shows menus for the Apps page.
+ /// </summary>
+ OptionButton MenuButton { get; }
+
+ /// <summary>
+ /// The Widget button which transits to the Widgets page.
+ /// </summary>
+ OptionButton WidgetsButton { get; }
+
+ /// <summary>
+ /// The Apps Page scroll
+ /// </summary>
+ AppsPageScrollView PageScroller { get; }
+
+ /// <summary>
+ /// The Chooser bar which will be shown in the Choose mode with "Done" and "Cancel" buttons.
+ /// </summary>
+ ChooserTopBar ChooserBar { get; }
+
+ /// <summary>
+ /// The Folder view which shows a folder's name and apps in it.
+ /// </summary>
+ FolderLayout FolderView { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+
+namespace Homescreen.View.Interface
+{
+ /// <summary>
+ /// An interface for status of the Apps page.
+ /// This interface provides interfaces of layouts and a state handler.
+ /// Also, provides current State and required APIs to be implemented in the states.
+ /// </summary>
+ public interface IAppsState
+ {
+ /// <summary>
+ /// An interface of Apps page's layouts.
+ /// </summary>
+ IAppsLayout StateLayout { get; }
+
+ /// <summary>
+ /// An interface of the state handler.
+ /// </summary>
+ IAppsStateHandler StateHandler { get; }
+
+ /// <summary>
+ /// Apps page's current state
+ /// </summary>
+ AppsState State { get; }
+
+ /// <summary>
+ /// A method will be called while new state is starting.
+ /// </summary>
+ void StepIn();
+
+ /// <summary>
+ /// A method will be called while state is changing.
+ /// </summary>
+ void StepOut();
+
+ /// <summary>
+ /// A method will be called when the back key is pressed.
+ /// </summary>
+ void OnBack();
+
+ /// <summary>
+ /// A method will be called when the home key is pressed.
+ /// </summary>
+ void OnHome();
+
+ /// <summary>
+ /// A method will be called when the menu key is pressed.
+ /// </summary>
+ void OnMenu();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Xamarin.Forms;
+
+namespace Homescreen.View.Interface
+{
+ /// <summary>
+ /// An interface to change an Apps page state and get each state among Apps page states
+ /// </summary>
+ public interface IAppsStateHandler
+ {
+ /// <summary>
+ /// A method provides an Apps Normal state.
+ /// </summary>
+ /// <returns>A Normal State</returns>
+ IAppsState GetNormalState();
+
+ /// <summary>
+ /// A method provides an Apps Edit state.
+ /// </summary>
+ /// <returns>An Edit State</returns>
+ IAppsState GetEditState();
+
+ /// <summary>
+ /// A method provides an Apps Drag state.
+ /// </summary>
+ /// <returns>A Drag State</returns>
+ IAppsState GetDragState();
+
+ /// <summary>
+ /// A method provides an Apps Choose state.
+ /// </summary>
+ /// <returns>A Choose State</returns>
+ IAppsState GetChooseState();
+
+ /// <summary>
+ /// A method provides a Previous state.
+ /// </summary>
+ /// <returns>A Previous State</returns>
+ IAppsState GetPreviousState();
+
+ /// <summary>
+ /// A method to set new state
+ /// </summary>
+ /// <param name="newState">A new state</param>
+ void SetState(IAppsState newState);
+ }
+}
--- /dev/null
+namespace Homescreen.View
+{
+ /// <summary>
+ /// An interface for key event handlers.
+ /// </summary>
+ public interface IBackHomeSignalReceiver
+ {
+ /// <summary>
+ /// A method will be called if the back key is pressed.
+ /// </summary>
+ void OnBackKeyPressed();
+
+ /// <summary>
+ /// A method will be called if the home key is pressed.
+ /// </summary>
+ void OnHomeKeyPressed();
+
+ /// <summary>
+ /// A method will be called if the menu key is pressed.
+ /// </summary>
+ void OnMenuKeyPressed();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// A delegate used for the pop-up menu operation.
+ /// </summary>
+ public delegate void ItemSelected();
+
+ /// <summary>
+ /// An interface to make Pop-up menu.
+ /// </summary>
+ public interface IMenuPopup
+ {
+ /// <summary>
+ /// A dismissed status of the IMenuPopup.
+ /// </summary>
+ bool IsDismissed { get; }
+
+ /// <summary>
+ /// A method adds a pop-up menu item.
+ /// </summary>
+ /// <param name="title">A menu title</param>
+ /// <param name="itemSelected">A function will be called if the menu is selected.</param>
+ void AddMenuItem(string title, ItemSelected itemSelected);
+
+ /// <summary>
+ /// Show a pop-up menu.
+ /// </summary>
+ /// <param name="anchor">A view can be base of pop-up menu.</param>
+ void Show(Xamarin.Forms.View anchor);
+
+ /// <summary>
+ /// Hide a pop-up menu.
+ /// </summary>
+ void Hide();
+ }
+
+ /// <summary>
+ /// OptionMenuPopup provides a pop-up menu
+ /// by a platform's feature vis IMenuPopup interface.
+ /// </summary>
+ public class OptionMenuPopup
+ {
+ private IMenuPopup popup;
+
+ /// <summary>
+ /// A dismissed status of the IMenuPopup.
+ /// </summary>
+ public bool IsDismissed
+ {
+ get
+ {
+ return popup.IsDismissed;
+ }
+ }
+
+ /// <summary>
+ /// A constructor which gets an instance of the IMenuPopup.
+ /// </summary>
+ public OptionMenuPopup()
+ {
+ popup = DependencyService.Get<IMenuPopup>();
+ }
+
+ /// <summary>
+ /// A method adds a pop-up menu item.
+ /// </summary>
+ /// <param name="title">A menu title</param>
+ /// <param name="itemSelected">A function will be called if the menu is selected.</param>
+ public void AddMenuItem(string title, ItemSelected itemSelected)
+ {
+ popup.AddMenuItem(title, itemSelected);
+ }
+
+ /// <summary>
+ /// Show a pop-up menu.
+ /// </summary>
+ /// <param name="anchor">A view can be base of pop-up menu.</param>
+ public void Show(Xamarin.Forms.View anchor)
+ {
+ popup.Show(anchor);
+ }
+
+ /// <summary>
+ /// Hide a pop-up menu.
+ /// </summary>
+ public void Hide()
+ {
+ popup.Hide();
+ }
+ }
+}
--- /dev/null
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ public class MouseEventArgs : EventArgs
+ {
+ public Point Down { get; set; } = new Point { X = 0, Y = 0 };
+ public Point Current { get; set; } = new Point { X = 0, Y = 0 };
+
+ private Point offset = new Point { X = 0, Y = 0 };
+ public Point Offset
+ {
+ get
+ {
+ offset.X = Current.X - Down.X;
+ offset.Y = Current.Y - Down.Y;
+ return offset;
+ }
+ }
+ }
+
+ public interface IMouseEventReceiver
+ {
+ EventHandler MouseDown { get; }
+ EventHandler MouseUp { get;}
+ EventHandler MouseMove { get; }
+ EventHandler MouseHold { get; }
+ EventHandler MouseClick { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.View.Interface
+{
+ /// <summary>
+ /// An interface for displaying a toast pop-up.
+ /// </summary>
+ public interface IToastPopup
+ {
+ /// <summary>
+ /// Display a toast pop-up with a given text.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ void Display(string text);
+
+ /// <summary>
+ /// Display a toast pop-up with a give text during given duration.
+ /// </summary>
+ /// <param name="text">A display text</param>
+ /// <param name="duration">A display time in second.</param>
+ void Display(string text, int duration);
+ }
+}
--- /dev/null
+namespace Homescreen.View.Interface
+{
+ public interface IVisibilityChanged : IBackHomeSignalReceiver
+ {
+ void Show(bool animation = true);
+
+ void Hide(bool animation = true);
+ }
+}
--- /dev/null
+namespace Homescreen.View.Widgets
+{
+ public interface IWidgetsLayout
+ {
+ OptionButton MenuButton { get; }
+ OptionButton AppsButton { get; }
+ WidgetPageScrollView PageScroller { get; }
+ AllpageLayout AllpageLayout { get; }
+ }
+}
--- /dev/null
+using System;
+using System.Threading.Tasks;
+
+namespace Homescreen.View.Widgets
+{
+ public interface IWidgetsState
+ {
+ IWidgetsLayout StateLayout { get; }
+ IWidgetsStateHandler StateHandler { get; }
+ WidgetsState State { get; }
+
+ bool ChangeableState(WidgetsState next);
+
+ Task StepIn(Enum args = null);
+ Task StepOut(Enum args = null);
+ void OnBack();
+ void OnHome();
+ void OnMenu();
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Homescreen.View.Widgets
+{
+ public enum WidgetsState
+ {
+ Normal,
+ Edit,
+ Reorder,
+ Allpage,
+ }
+
+ public interface IWidgetsStateHandler
+ {
+ void SetState(WidgetsState state, Enum args = null);
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<Image xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.NinePatch">
+</Image>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// NinePath class
+ /// </summary>
+ public partial class NinePatch : Image
+ {
+ /// <summary>
+ /// Identifies the BorderLeft bind-able property
+ /// </summary>
+ public static readonly BindableProperty BorderLeftProperty = BindableProperty.Create("BorderLeft", typeof(int), typeof(NinePatch), default(int));
+
+ /// <summary>
+ /// Gets or sets left border of NinePatchImage
+ /// </summary>
+ public int BorderLeft
+ {
+ get { return (int)GetValue(BorderLeftProperty); }
+ set { SetValue(BorderLeftProperty, value); }
+ }
+
+ /// <summary>
+ /// Identifies the BorderRight bind-able property
+ /// </summary>
+ public static readonly BindableProperty BorderRightProperty = BindableProperty.Create("BorderRight", typeof(int), typeof(NinePatch), default(int));
+
+ /// <summary>
+ /// Gets or sets right border of NinePatchImage
+ /// </summary>
+ public int BorderRight
+ {
+ get { return (int)GetValue(BorderRightProperty); }
+ set { SetValue(BorderRightProperty, value); }
+ }
+
+ /// <summary>
+ /// Identifies the BorderTop bind-able property
+ /// </summary>
+ public static readonly BindableProperty BorderTopProperty = BindableProperty.Create("BorderTop", typeof(int), typeof(NinePatch), default(int));
+
+ /// <summary>
+ /// Gets or sets top border of NinePatchImage
+ /// </summary>
+ public int BorderTop
+ {
+ get { return (int)GetValue(BorderTopProperty); }
+ set { SetValue(BorderTopProperty, value); }
+ }
+
+ /// <summary>
+ /// Identifies the BorderBottom bind-able property
+ /// </summary>
+ public static readonly BindableProperty BorderBottomProperty = BindableProperty.Create("BorderBottom", typeof(int), typeof(NinePatch), default(int));
+
+ /// <summary>
+ /// Gets or sets bottom border of NinePatchImage
+ /// </summary>
+ public int BorderBottom
+ {
+ get { return (int)GetValue(BorderBottomProperty); }
+ set { SetValue(BorderBottomProperty, value); }
+ }
+
+ /// <summary>
+ /// A constructor
+ /// </summary>
+ public NinePatch()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.OptionButton">
+ <view:ClickableImage
+ x:Name="background"
+ Source="home_button_bg.png"
+ Opacity="0.2"
+ NormalColor="Black"
+ Aspect="AspectFit"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All"/>
+ <Image
+ x:Name="icon"
+ InputTransparent="True"
+ Aspect="AspectFit"
+ AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.8, 0.8"
+ AbsoluteLayout.LayoutFlags="All"/>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.Windows.Input;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Class for OptionButton inherited AbsoluteLayout
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class OptionButton : AbsoluteLayout
+ {
+ /// <summary>
+ /// Property for icon image source
+ /// </summary>
+ public ImageSource IconImageSource { get => icon.Source; set => icon.Source = value; }
+
+ /// <summary>
+ /// Property to deal with clicked event with command
+ /// </summary>
+ public ICommand OnClickedCommand { get; set; }
+ public bool IsLock => Opacity < 1.0;
+
+ /// <summary>
+ /// Constructor for OptionButton
+ /// </summary>
+ public OptionButton()
+ {
+ InitializeComponent();
+
+ background.Clicked += (s, e) =>
+ {
+ background.Scale = 1.0;
+
+ if (!IsLock)
+ {
+ OnClickedCommand?.Execute(String.Empty);
+ }
+ };
+
+ background.Pressed += (s, e) =>
+ {
+ background.Scale = 0.8;
+ background.ScaleTo(1.0, 100, Easing.Linear);
+ };
+
+ background.Released += (s, e) =>
+ {
+ Scale = 1.0;
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Homescreen.View.PageIndicator"
+ PageCount = "{Binding PageCount, Mode=OneWay}"
+ ScrollPosition = "{Binding ScrollPosition, Mode=OneWay}">
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// A Page Indicator EventArgs
+ /// </summary>
+ public class IndicatorClickedEventArgs : EventArgs
+ {
+ public int ClickedPage { get; set; }
+ }
+
+ /// <summary>
+ /// A Page Indicator
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class PageIndicator : AbsoluteLayout
+ {
+ private const int MaxUnitCount = 7;
+ private const double UnitWidth = (1.0 / MaxUnitCount);
+
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(PageIndicator), defaultValue: 0, defaultBindingMode: BindingMode.OneWay);
+
+ public static readonly BindableProperty ScrollPositionProperty = BindableProperty.Create(
+ "ScrollPosition", typeof(double), typeof(PageIndicator), defaultValue: 0.0, defaultBindingMode: BindingMode.OneWay);
+
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ public double ScrollPosition
+ {
+ get => (double)GetValue(ScrollPositionProperty);
+ set => SetValue(ScrollPositionProperty, value);
+ }
+
+ public event EventHandler Clicked;
+
+ public PageIndicator()
+ {
+ InitializeComponent();
+
+ UpdateUnitCount();
+ UpdateUnitPosition();
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == PageCountProperty.PropertyName)
+ {
+ UpdateUnitCount();
+
+ UpdateUnitPosition();
+ }
+ else if (propertyName == ScrollPositionProperty.PropertyName)
+ {
+ UpdateUnitPosition();
+ }
+ }
+
+ private void UpdateUnitCount()
+ {
+ if (Children.Count < PageCount && Children.Count < MaxUnitCount)
+ {
+ for (int i = Children.Count; i < PageCount && i < MaxUnitCount; i++)
+ {
+ var unit = new IndicatorUnit(i);
+ unit.Clicked += (s, e) =>
+ {
+ IndicatorUnit u = s as IndicatorUnit;
+ Clicked?.Invoke(this, new IndicatorClickedEventArgs { ClickedPage = u.PageIndex });
+ };
+
+ Children.Add(unit);
+ }
+ }
+ else if (Children.Count > PageCount)
+ {
+ for (int i = Children.Count; i > PageCount; i--)
+ {
+ Children.RemoveAt(Children.Count - 1);
+ }
+ }
+
+ if (PageCount > MaxUnitCount)
+ {
+ IndicatorUnit center = Children[MaxUnitCount / 2] as IndicatorUnit;
+ center.Rotation = 0.0;
+ center.IsCenter = true;
+ }
+ else
+ {
+ foreach (IndicatorUnit unit in Children)
+ {
+ unit.IsCenter = false;
+ }
+ }
+
+ for (int idx = 0; idx < Children.Count; idx++)
+ {
+ var unit = Children[idx] as IndicatorUnit;
+ unit.PageIndex = GetPageIndex(idx);
+ }
+ }
+
+ private void UpdateUnitPosition()
+ {
+ for (int i = 0; i < Children.Count; i++)
+ {
+ IndicatorUnit unit = Children[i] as IndicatorUnit;
+ unit.PageIndex = GetPageIndex(i);
+ AbsoluteLayout.SetLayoutBounds(unit, new Rectangle(GetX(i, Children.Count), 0.5, UnitWidth * 0.9, 0.5));
+ AbsoluteLayout.SetLayoutFlags(unit, AbsoluteLayoutFlags.All);
+ }
+
+ for (int pageIndex = 0; pageIndex < PageCount; pageIndex++)
+ {
+ IndicatorUnit unit = Children[GetUnitIndex(pageIndex)] as IndicatorUnit;
+
+ double pageWidth = (1.0 / PageCount);
+ double pageLeft = unit.IsCenter ? (pageWidth * ((double)MaxUnitCount / 2.0)) : ((double)pageIndex / PageCount);
+ double pageRight = unit.IsCenter ? pageLeft + (pageWidth * (PageCount - MaxUnitCount + 1)) : pageLeft + pageWidth;
+
+ if (pageLeft == ScrollPosition)
+ {
+ unit.Rotation = 90;
+ unit.Opacity = 1.0;
+ }
+ else if (pageLeft - ScrollPosition >= pageWidth)
+ {
+ unit.Rotation = 0;
+ unit.Opacity = 0.3;
+ }
+ else if (ScrollPosition >= pageRight)
+ {
+ unit.Rotation = 0;
+ unit.Opacity = 0.3;
+ }
+ else if (pageLeft <= ScrollPosition && ScrollPosition <= pageRight - pageWidth)
+ {
+ unit.Rotation = 90;
+ unit.Opacity = 1.0;
+ }
+ else if (ScrollPosition < pageLeft)
+ {
+ unit.Rotation = (1.0 - (pageLeft - ScrollPosition) / pageWidth) * 90;
+ unit.Opacity = 1.0 - Math.Abs(90 - unit.Rotation) * 0.008;
+ }
+ else if (ScrollPosition < pageRight)
+ {
+ unit.Rotation = (1.0 - (pageRight - ScrollPosition) / pageWidth) * 90 + 90;
+ unit.Opacity = 1.0 - Math.Abs(90 - unit.Rotation) * 0.008;
+ }
+
+ if (Math.Abs(unit.Rotation - 0.0) <= 1.0 ||
+ Math.Abs(unit.Rotation - 180.0) <= 1.0)
+ {
+ unit.Rotation = 0.0;
+ }
+ else if (Math.Abs(unit.Rotation - 90) <= 1.0)
+ {
+ unit.Rotation = 90.0;
+ }
+
+ if (Math.Abs(unit.Opacity - 1.0) <= 1E-2)
+ {
+ unit.Opacity = 1.0;
+ }
+ else if (unit.Opacity < 0.3 || Math.Abs(unit.Opacity - 0.3) <= 1E-2)
+ {
+ unit.Opacity = 0.3;
+ }
+ }
+
+ if (PageCount > MaxUnitCount)
+ {
+ IndicatorUnit center = Children[MaxUnitCount / 2] as IndicatorUnit;
+ center.Text = $"{(int)(ScrollPosition * PageCount) + 1}";
+ }
+ }
+
+ private int GetUnitIndex(int pageIndex)
+ {
+ if (PageCount <= Children.Count ||
+ pageIndex <= 2)
+ {
+ return pageIndex;
+ }
+ else if (PageCount - pageIndex - 1 <= 2)
+ {
+ return MaxUnitCount - (PageCount - pageIndex - 1) - 1;
+ }
+
+ return MaxUnitCount / 2;
+ }
+
+ private int GetPageIndex(int unitIndex)
+ {
+ if (PageCount <= MaxUnitCount || unitIndex < MaxUnitCount / 2)
+ {
+ return unitIndex;
+ }
+
+ if (unitIndex > MaxUnitCount / 2)
+ {
+ return PageCount - (MaxUnitCount - unitIndex);
+ }
+
+ return (PageCount - 1) / 2;
+ }
+
+ private int GetCurrentPageIndex()
+ {
+ int page = 0;
+ for (double i = 0.0; i <= ScrollPosition; i += (1.0 / PageCount))
+ {
+ page += 1;
+ }
+
+ return page - 1;
+ }
+
+ private double GetX(int index, int count)
+ {
+ return ((7 - count) / 2 + index) * (1.0 / (MaxUnitCount - 1))
+ + ((count % 2 == 0) ? (1.0 / ((MaxUnitCount - 1) * 2)) : 0);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<Image xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:viewmodel="clr-namespace:Homescreen.ViewModel"
+ x:Class="Homescreen.View.Wallpaper"
+ Source="{Binding ImagePath}"
+ Aspect="AspectFill">
+ <Image.BindingContext>
+ <viewmodel:WallpaperPath/>
+ </Image.BindingContext>
+</Image>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View
+{
+ /// <summary>
+ /// Wallpaper is a background image of the home screen
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class Wallpaper : Image
+ {
+ /// <summary>
+ /// A constructor of the Wallpaper class.
+ /// </summary>
+ public Wallpaper()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ x:Class="Homescreen.View.Widgets.AddWidgetPage"
+ BackgroundColor="White"
+ AddWidgetCommand="{Binding AddWidgetCommand}">
+ <ContentPage.Content>
+ <widgets:FastScrollLayout
+ x:Name="scrollLayout">
+ <view:HomeScrollView
+ x:Name="scroller"
+ AbsoluteLayout.LayoutBounds=".0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All">
+ <StackLayout
+ x:Name="widgetListLayout"
+ Orientation="Vertical"
+ Spacing="0"/>
+ </view:HomeScrollView>
+ </widgets:FastScrollLayout>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for AddWidgetPage inherited from ContentPage and implements IBackHomeSignalReceiver
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AddWidgetPage : ContentPage, IBackHomeSignalReceiver
+ {
+ /// <summary>
+ /// BindableProperty to make binding with AddWidgetCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty AddWidgetCommandProperty
+ = BindableProperty.Create("AddWidgetCommand", typeof(Command), typeof(AddWidgetPage));
+
+ /// <summary>
+ /// An accessors for AddWidgetCommand property
+ /// </summary>
+ public Command AddWidgetCommand
+ {
+ get => (Command)GetValue(AddWidgetCommandProperty);
+ set => SetValue(AddWidgetCommandProperty, value);
+ }
+
+ public FastScrollLayout ScrollLayout => scrollLayout;
+ public StackLayout WidgetListLayout => widgetListLayout;
+ public ScrollView Scroller => scroller;
+
+ private StatusBarMode previousBarMdoe;
+
+ /// <summary>
+ /// Constructor for AddWidgetPage
+ /// Adds all Widgets to AddWidgetPage
+ /// </summary>
+ public AddWidgetPage()
+ {
+ InitializeComponent();
+
+ AddWidgets();
+
+ ScrollLayout.IndexSelected += ((s, e) =>
+ {
+ if (e is IndexSelectedArgs arg)
+ {
+ ScrollTo(arg.Key);
+ }
+ });
+ }
+
+ /// <summary>
+ /// Scrolls to the widget which have same first character as key
+ /// </summary>
+ /// <param name="key">Target character to be found</param>
+ private async void ScrollTo(string key)
+ {
+ foreach (var item in widgetListLayout.Children)
+ {
+ if (item is ItemLayout itemLayout)
+ {
+ if (itemLayout.Title.Length > 0 && itemLayout.Title.Substring(0, 1) == key)
+ {
+ await scroller.ScrollToAsync(item, ScrollToPosition.Start, true);
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Adds all Widgets in widget list using DependencyService
+ /// </summary>
+ private void AddWidgets()
+ {
+ var widgetList = DependencyService.Get<IWidgetManager>()?.WidgetList;
+ widgetList.Sort((WidgetInformation a, WidgetInformation b) => { return a.Name.CompareTo(b.Name); });
+
+ Color itemColor = new Color(0.98, 0.98, 0.98);
+ string previousWidgetName = null;
+ int count = 0;
+ ItemLayout itemLayout = null;
+ foreach (var widgetInfo in widgetList)
+ {
+ if (previousWidgetName == null || previousWidgetName != widgetInfo.Name)
+ {
+ previousWidgetName = widgetInfo.Name;
+
+ ItemLayout title = new ItemLayout() { BackgroundColor = itemColor };
+ title.Children.Add(new ItemTitleLabel() { Text = $"{widgetInfo.Name}" });
+
+ widgetListLayout.Children.Add(title);
+ count = 0;
+ }
+
+ var preview = new PreviewImage()
+ {
+ Type = widgetInfo.Type,
+ Source = widgetInfo.PreviewImagePath,
+ };
+
+ preview.Clicked += (s, e) =>
+ {
+ var widget = new WidgetInformationToAdd(new WidgetInformation()
+ {
+ PackageId = widgetInfo.PackageId,
+ Name = widgetInfo.Name,
+ WidgetId = widgetInfo.WidgetId,
+ Type = widgetInfo.Type,
+ PageIndex = -1,
+ X = 0,
+ Y = 0,
+ });
+
+ widget.FailedToAdd += (sender, args) =>
+ {
+ var popup = new OneButtonPopup()
+ {
+ Title = "Unable to add widget",
+ Content = new Label()
+ {
+ Text = "Unable to add this Home Box to the Homescreen. There is not enough space on the Home screen. Remove some Home Boxes and try again.",
+ FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
+ Margin = new Thickness(DeviceInfo.Instance.Width * 0.04, 0, DeviceInfo.Instance.Width * 0.04, 0),
+ },
+ };
+ var okButton = new Button() { Text = "OK" };
+ okButton.Clicked += (button, arg) =>
+ {
+ popup.Hide();
+ };
+ popup.FirstButton = okButton;
+
+ popup.Show();
+ };
+ AddWidgetCommand.Execute(widget);
+
+ Navigation.PopAsync();
+ };
+
+ if ((count % 2) == 0)
+ {
+ itemLayout = new ItemLayout() { BackgroundColor = itemColor };
+
+ itemColor = new Color(itemColor.R * 0.972, itemColor.G * 0.98, itemColor.B * 0.99);
+
+ count += 1;
+ }
+
+ itemLayout?.Children.Add(preview);
+
+ widgetListLayout.Children.Add(itemLayout);
+ }
+ }
+
+ /// <summary>
+ /// This method will be called when this page is shown
+ /// If you do not change the StatusBar mode, the Title and StatusBar will overlap.
+ /// It would be best to increase the size of the TitleBar, but I can not find a way.
+ /// There is a blinking problem because the StatusBar is slower than the page disappearing speed.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ previousBarMdoe = DeviceInfo.Instance.StatusBarMode;
+
+ DeviceInfo.Instance.StatusBarMode = StatusBarMode.Transparent;
+
+ base.OnAppearing();
+ }
+ /// <summary>
+ /// This method will be called when this page turn to be hidden
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ DeviceInfo.Instance.StatusBarMode = previousBarMdoe;
+
+ base.OnDisappearing();
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed on AddWidgetPage
+ /// </summary>
+ public void OnBackKeyPressed()
+ {
+ Navigation.PopAsync();
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed on AddWidgetPage
+ /// </summary>
+ public void OnHomeKeyPressed()
+ {
+ Navigation.PopAsync();
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed on AddWidgetPage
+ /// </summary>
+ public void OnMenuKeyPressed()
+ {
+
+ }
+ }
+
+ /// <summary>
+ /// Class for ItemLayout used in AddWidgetPage
+ /// </summary>
+ public class ItemLayout : StackLayout
+ {
+ public string Title
+ {
+ get
+ {
+ foreach (var child in Children)
+ {
+ if (child is Label label)
+ {
+ return label.Text;
+ }
+ }
+
+ return "";
+ }
+ }
+
+ /// <summary>
+ /// Constructor for ItemLayout
+ /// </summary>
+ public ItemLayout()
+ {
+ Orientation = StackOrientation.Horizontal;
+ Padding = new Thickness(DeviceInfo.Instance.Width * 0.025, 0, 0, 0);
+ Spacing = DeviceInfo.Instance.Width * 0.015;
+ }
+ }
+
+ /// <summary>
+ /// Class for ItemTitleLabel inherited from Label
+ /// </summary>
+ class ItemTitleLabel : Label
+ {
+ /// <summary>
+ /// Constructor for ItemTitleLabel
+ /// </summary>
+ public ItemTitleLabel()
+ {
+ FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label));
+ VerticalOptions = LayoutOptions.Center;
+ VerticalTextAlignment = TextAlignment.Center;
+ TextColor = new Color(0.596, 0.596, 0.596);
+ Margin = new Thickness(0, DeviceInfo.Instance.Width * 0.025, 0, 0);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ xmlns:view="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Widgets.AllpageLayout"
+ x:Name="allpageLayout"
+ Opacity="0.0"
+ ItemsSource="{Binding WidgetsInformation}"
+ AddWidgetPageCommand="{Binding AddWidgetPageCommand}"
+ ScrollToPageCommand="{Binding ScrollToPageCommand}">
+ <AbsoluteLayout.Resources>
+ <ResourceDictionary>
+ <widgets:AddButtonPositionConverter x:Key="positionConverter"/>
+ <widgets:AddButtonVisibleConverter x:Key="visibleConverter"/>
+ </ResourceDictionary>
+ </AbsoluteLayout.Resources>
+ <view:ClickableImage x:Name="add"
+ Source="all_page_add.png"
+ PressedSource="all_page_add_press.png"
+ IsVisible="{Binding PageCount, Converter={StaticResource visibleConverter}}"
+ AbsoluteLayout.LayoutBounds="{Binding PageCount, Converter={StaticResource positionConverter}}"
+ AbsoluteLayout.LayoutFlags="All"/>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model;
+using System;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Collections.Specialized;
+using Homescreen.Debug;
+using Homescreen.ViewModel.Widgets;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// This is the Converter class for converting the value of PageCount to the location of the Add button.
+ /// </summary>
+ public class AddButtonPositionConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ int pageCount = System.Convert.ToInt32(value);
+ double x = 0.5;
+ double y = 0.5;
+
+ x = (pageCount % 2) == 0 ? 0.2375 : 1.0 - 0.2375;
+ if (pageCount >= 4)
+ {
+ y = 0.85;
+ }
+ else if (pageCount >= 2)
+ {
+ y = 0.675;
+ }
+
+ return new Rectangle(x, y, 0.11111, 0.0625);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// This is the Converter class for converting the value of PageCount to the visibility of the Add button.
+ /// </summary>
+ public class AddButtonVisibleConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ int pageCount = System.Convert.ToInt32(value);
+ return pageCount < 6;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Class for AllpageLayout inherited from AbsoluteLayout
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AllpageLayout : AbsoluteLayout
+ {
+ /// <summary>
+ /// BindableProperty to make binding with ItemsSource in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty ItemsSourceProperty
+ = BindableProperty.Create("ItemsSource", typeof(ObservableCollection<WidgetPageInformation>),
+ typeof(AllpageLayout), default(ObservableCollection<WidgetPageInformation>));
+
+ /// <summary>
+ /// BindableProperty to make binding with PageCount in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(AllpageLayout), defaultValue: 0, defaultBindingMode: BindingMode.OneWay);
+
+ /// <summary>
+ /// BindableProperty to make binding with AddWidgetPageCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty AddWidgetPageCommandProperty
+ = BindableProperty.Create("AddWidgetPageCommand", typeof(Command), typeof(AllpageLayout));
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollToPageCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(AllpageLayout));
+
+ /// <summary>
+ /// An accessors for ItemsSource
+ /// </summary>
+ public ObservableCollection<WidgetPageInformation> ItemsSource
+ {
+ get { return (ObservableCollection<WidgetPageInformation>)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ /// <summary>
+ /// An accessors for PageCount
+ /// </summary>
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for AddWidgetPageCommand
+ /// </summary>
+ public Command AddWidgetPageCommand
+ {
+ get => (Command)GetValue(AddWidgetPageCommandProperty);
+ set => SetValue(AddWidgetPageCommandProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollToPageCommand
+ /// </summary>
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+
+ /// <summary>
+ /// an event handler to signal that all page layout should be terminated.
+ /// </summary>
+ public event EventHandler Closed;
+
+ /// <summary>
+ /// Constructor for AllpageLayout and in this constructor draw all pages
+ /// </summary>
+ public AllpageLayout()
+ {
+ InitializeComponent();
+
+ add.Clicked += async (s, e) =>
+ {
+ add.Opacity = 0;
+ AddWidgetPageCommand.Execute(null);
+
+ await Task.Delay(50);
+ await add.FadeTo(1.0, 200);
+ };
+ }
+
+ /// <summary>
+ /// Hide all page layout.
+ /// </summary>
+ /// <returns>A task instance of this method.</returns>
+ public async Task Hide()
+ {
+ foreach (var layout in Children)
+ {
+ if (layout is AllpageThumbnailLayout thumbnail)
+ {
+ thumbnail.Return();
+ }
+ }
+
+ for (int index = 0; index < ItemsSource.Count; index++)
+ {
+ for (int target = index + 1; target < ItemsSource.Count; target++)
+ {
+ if (ItemsSource[target] is WidgetPageInformation page)
+ {
+ if (page.PageIndex == index)
+ {
+ ItemsSource.Move(target, index);
+ break;
+ }
+ }
+ }
+ }
+
+ await this.FadeTo(0.0, 200);
+ }
+
+ /// <summary>
+ /// Show all page layout.
+ /// </summary>
+ /// <returns>A task instance of this method.</returns>
+ public async Task Show()
+ {
+ await this.FadeTo(1.0, 200);
+ }
+
+ private void AddThumbnail(WidgetPageInformation info)
+ {
+ var thumbnail = new AllpageThumbnailLayout(info);
+ thumbnail.Clicked += (s, e) =>
+ {
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { GoalPageIndex = thumbnail.PageIndex});
+ Closed?.Invoke(this, EventArgs.Empty);
+ };
+ thumbnail.Reordered += ShuffleThumbnail;
+
+ Children.Add(thumbnail);
+ }
+
+ private void ShuffleThumbnail(object sender, EventArgs e)
+ {
+ if (e is ReorderedEventArgs args)
+ {
+ foreach (var layout in Children)
+ {
+ if (layout is AllpageThumbnailLayout thumbnail)
+ {
+ if (args.From == thumbnail.PageIndex)
+ {
+ thumbnail.Move(args.To);
+ }
+ else if (args.From < thumbnail.PageIndex && thumbnail.PageIndex <= args.To)
+ {
+ thumbnail.Move(thumbnail.PageIndex - 1);
+ }
+ else if (args.To <= thumbnail.PageIndex && thumbnail.PageIndex < args.From)
+ {
+ thumbnail.Move(thumbnail.PageIndex + 1);
+ }
+ else
+ {
+ thumbnail.Move(thumbnail.PageIndex);
+ }
+ }
+ }
+ }
+ }
+
+ private void RemoveThumbnail(WidgetPageInformation info)
+ {
+ foreach (var layout in Children)
+ {
+ if (layout is AllpageThumbnailLayout thumbnail && thumbnail.PageInfo == info)
+ {
+ Children.Remove(thumbnail);
+ break;
+ }
+ }
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == ItemsSourceProperty.PropertyName)
+ {
+ foreach (var item in ItemsSource)
+ {
+ AddThumbnail(item);
+ }
+
+ ItemsSource.CollectionChanged += ItemsSourceChanged;
+ }
+ }
+
+ private void ItemsSourceChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ if (e.NewItems[0] is WidgetPageInformation pageInfo)
+ {
+ AddThumbnail(pageInfo);
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ if (e.OldItems[0] is WidgetPageInformation pageInfo)
+ {
+ RemoveThumbnail(pageInfo);
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ {
+ if (e.OldItems[0] is WidgetPageInformation oldInfo)
+ {
+ RemoveThumbnail(oldInfo);
+ }
+
+ if (e.NewItems[0] is WidgetPageInformation newInfo)
+ {
+ AddThumbnail(newInfo);
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ {
+ Children.Clear();
+ }
+
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.ViewModel;
+using Xamarin.Forms;
+using System.Threading.Tasks;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for AllPageState state of Widgets inherited from IWidgetsState
+ /// </summary>
+ public class AllpageState : IWidgetsState
+ {
+ /// <summary>
+ /// Property for WidgetLayout to execute some behavior regarding with change of state
+ /// </summary>
+ public IWidgetsLayout StateLayout { get; private set; }
+ /// <summary>
+ /// Property for IWidgetsStateHandler to set each state
+ /// </summary>
+ public IWidgetsStateHandler StateHandler { get; private set; }
+ public WidgetsState State => WidgetsState.Allpage;
+
+ private StatusBarMode previousBarMdoe;
+
+ /// <summary>
+ /// This method will be called when Widgets enter AllPage State
+ /// </summary>
+ /// <param name="args">null</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepIn(Enum args = null)
+ {
+ previousBarMdoe = DeviceInfo.Instance.StatusBarMode;
+ DeviceInfo.Instance.StatusBarMode = StatusBarMode.Transparent;
+
+ StateLayout.MenuButton.IsVisible = false;
+ StateLayout.AppsButton.IsVisible = false;
+ StateLayout.PageScroller.IsVisible = false;
+
+ StateLayout.AllpageLayout.IsVisible = true;
+ StateLayout.AllpageLayout.Closed += GoToNormalState;
+
+ await StateLayout.AllpageLayout.Show();
+ }
+
+ /// <summary>
+ /// This method will be called Widgets exit from AllPage State
+ /// </summary>
+ /// <param name="args">Next WidgetsState</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepOut(Enum args = null)
+ {
+ StateLayout.AllpageLayout.Closed -= GoToNormalState;
+
+ await StateLayout.AllpageLayout.Hide();
+ StateLayout.AllpageLayout.IsVisible = false;
+
+ DeviceInfo.Instance.StatusBarMode = previousBarMdoe;
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed while in AllPage state
+ /// </summary>
+ public void OnBack()
+ {
+ StateHandler.SetState(WidgetsState.Normal);
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed while in AllPage state
+ /// </summary>
+ public void OnHome()
+ {
+ StateHandler.SetState(WidgetsState.Normal, NormalState.StateOption.ToHome);
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed while in AllPage state
+ /// </summary>
+ public void OnMenu()
+ {
+ }
+
+ /// <summary>
+ /// Constructor for AllPageState
+ /// </summary>
+ /// <param name="stateHandler">WidgetsLayout to set each state</param>
+ /// <param name="widgetLayout">WidgetsLayout</param>
+ public AllpageState(IWidgetsStateHandler stateHandler, IWidgetsLayout widgetLayout)
+ {
+ StateHandler = stateHandler;
+ StateLayout = widgetLayout;
+ }
+
+ /// <summary>
+ /// This method will be called when mouse up event is occurred while in AllPage state
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Argument regarding event</param>
+ public void MouseUp(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Normal);
+ }
+
+ /// <summary>
+ /// Check whether the Widgets can be changed to another state or not
+ /// </summary>
+ /// <param name="next">Next state</param>
+ /// <returns>True or not</returns>
+ public bool ChangeableState(WidgetsState next)
+ {
+ if (next == WidgetsState.Normal)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void GoToNormalState(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Normal);
+ }
+
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ x:Class="Homescreen.View.Widgets.AllpageThumbnailLayout"
+ x:Name="thumbnailLayout"
+ PageCount = "{Binding PageCount, Mode=OneWay}"
+ DeleteWidgetPageCommand="{Binding DeleteWidgetPageCommand}"
+ UpdateWidgetCommand="{Binding UpdateWidgetCommand}">
+ <AbsoluteLayout x:Name="edge"
+ IsVisible="False"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All">
+ <BoxView Color="White"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, .02"
+ AbsoluteLayout.LayoutFlags="All"/>
+ <BoxView Color="White"
+ AbsoluteLayout.LayoutBounds="0, 1, 1, .02"
+ AbsoluteLayout.LayoutFlags="All"/>
+ <BoxView Color="White"
+ AbsoluteLayout.LayoutBounds="0, .5, .02, .96"
+ AbsoluteLayout.LayoutFlags="All"/>
+ <BoxView Color="White"
+ AbsoluteLayout.LayoutBounds="1, .5, .02, .96"
+ AbsoluteLayout.LayoutFlags="All"/>
+ </AbsoluteLayout>
+ <BoxView x:Name="bg"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All"
+ Color="Black"
+ Opacity="0.1"/>
+ <Grid x:Name="thumbnailBox"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="*" />
+ <RowDefinition Height="*" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ </Grid>
+ <view:ClickableImage x:Name="deleteButton"
+ Source="btn_delete_nor.png"
+ PressedSource="btn_delete_press.png"
+ AbsoluteLayout.LayoutBounds=".03846, .03846, .1875, .1875"
+ AbsoluteLayout.LayoutFlags="All">
+ </view:ClickableImage>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model;
+using System;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using System.Runtime.CompilerServices;
+using System.Collections.Specialized;
+using Homescreen.ViewModel;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// An event argument to convey the location where the thumbnail should be moved.
+ /// </summary>
+ public class ReorderedEventArgs : EventArgs
+ {
+ public int From { get; set; }
+ public int To { get; set; }
+ }
+
+ /// <summary>
+ /// Class for AllpageThumbnailLayout inherited from AbsoluteLayout and implement IMouseEventReceiver
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class AllpageThumbnailLayout : AbsoluteLayout, IMouseEventReceiver
+ {
+ /// <summary>
+ /// BindableProperty to make binding with PageCount in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(AllpageThumbnailLayout), defaultValue: 0, defaultBindingMode: BindingMode.OneWay);
+
+ /// <summary>
+ /// BindableProperty to make binding with DeleteWidgetPageCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty DeleteWidgetPageCommandProperty
+ = BindableProperty.Create("DeleteWidgetPageCommand", typeof(Command), typeof(AllpageThumbnailLayout));
+
+ /// <summary>
+ /// BindableProperty to make binding with UpdateWidgetCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty UpdateWidgetCommandProperty
+ = BindableProperty.Create("UpdateWidgetCommand", typeof(Command), typeof(AllpageThumbnailLayout));
+
+ /// <summary>
+ /// An accessors for PageCount
+ /// </summary>
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for DeleteWidgetPageCommand
+ /// </summary>
+ public Command DeleteWidgetPageCommand
+ {
+ get => (Command)GetValue(DeleteWidgetPageCommandProperty);
+ set => SetValue(DeleteWidgetPageCommandProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for UpdateWidgetCommand
+ /// </summary>
+ public Command UpdateWidgetCommand
+ {
+ get => (Command)GetValue(UpdateWidgetCommandProperty);
+ set => SetValue(UpdateWidgetCommandProperty, value);
+ }
+
+ private int pageIndex = -1;
+ /// <summary>
+ /// Property for the page index of this thumbnail
+ /// </summary>
+ public int PageIndex
+ {
+ get => pageIndex;
+ set
+ {
+ if (pageIndex != value)
+ {
+ pageIndex = value;
+ SetBounds();
+ }
+ }
+ }
+
+ private WidgetPageInformation pageInfo;
+ /// <summary>
+ /// Property for WidgetPageInformation
+ /// </summary>
+ public WidgetPageInformation PageInfo
+ {
+ get => pageInfo;
+ set
+ {
+ pageInfo = value;
+ PageIndex = pageInfo.PageIndex;
+ }
+ }
+
+ /// <summary>
+ /// EventHandler for MouseDown event
+ /// MouseDown property must be declared, because WidgetAllPageThumnail implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseDown { get; }
+ /// <summary>
+ /// EventHandler for MouseUp event
+ /// MouseDown property must be declared, because WidgetAllPageThumnail implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseUp { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseMove event
+ /// MouseDown property must be declared, because WidgetAllPageThumnail implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseMove { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseHold event
+ /// MouseDown property must be declared, because WidgetAllPageThumnail implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseHold { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseClick event
+ /// MouseDown property must be declared, because WidgetAllPageThumnail implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseClick { get; private set; }
+
+ /// <summary>
+ /// EventHandler for Thumbnail Click
+ /// </summary>
+ public event EventHandler Clicked;
+ /// <summary>
+ /// EventHandler for Thumbnail reordering
+ /// </summary>
+ public event EventHandler Reordered;
+
+ private bool IsPicked { get; set; }
+ private int reorededPosition;
+
+ /// <summary>
+ /// Constructor for AllpageThumbnailLayout
+ /// Create thumbnail of page with Widgets in it and register mouse event
+ /// </summary>
+ /// <param name="pageInfo">WidgetPageInformation</param>
+ public AllpageThumbnailLayout(WidgetPageInformation pageInfo)
+ {
+ InitializeComponent();
+
+ PageInfo = pageInfo;
+ PageInfo.PageIndexChanged += (s, e) =>
+ {
+ PageIndex = PageInfo.PageIndex;
+ };
+ PageInfo.Widgets.CollectionChanged += WidgetsChanged;
+ UpdateThumbnail();
+
+ deleteButton.Clicked += (s, e) => DeletePage();
+
+ MouseUp += (s, e) => Drop((e as MouseEventArgs));
+ MouseHold += (s, e) => PickUp((e as MouseEventArgs));
+ MouseMove += (s, e) => Drag((e as MouseEventArgs));
+ MouseClick += (s, e) => Clicked?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void SetBounds()
+ {
+ if (IsPicked == false)
+ {
+ TranslationX = 0;
+ TranslationY = 0;
+ SetLayoutBounds(this, GetBoundsByPageIndex(PageIndex));
+ SetLayoutFlags(this, AbsoluteLayoutFlags.All);
+ }
+ }
+
+ private Rectangle GetBoundsByPageIndex(int index)
+ {
+ double x = 0.5;
+ double y = 0.5;
+
+ x = (index % 2) == 0 ? 0.08 : 1.0 - 0.08;
+ if (PageCount >= 4)
+ {
+ if (index <= 1)
+ {
+ y = 0.0625;
+ }
+ else if (index >= 4)
+ {
+ y = 1.0 - 0.0625;
+ }
+ }
+ else if (PageCount >= 2)
+ {
+ if (index <= 1)
+ {
+ y = 0.28125;
+ }
+ else
+ {
+ y = 1 - 0.28125;
+ }
+ }
+
+ return new Rectangle(x, y, 0.44444, 0.25);
+ }
+
+ private Rectangle GetPixelBoundsByPageIndex(int index)
+ {
+ double x = (index % 2) == 0 ? 0.04444 : 1.0 - 0.48888;
+ double y = 0.5 - 0.125;
+ if (PageCount >= 4)
+ {
+ if (index <= 1)
+ {
+ y = 0.046875;
+ }
+ else if (index >= 4)
+ {
+ y = 1.0 - 0.296875;
+ }
+ }
+ else if (PageCount >= 2)
+ {
+ if (index <= 1)
+ {
+ y = 0.5 - 0.2890625;
+ }
+ else
+ {
+ y = 0.5 + 0.0390625;
+ }
+ }
+
+ return new Rectangle(x * DeviceInfo.Instance.Width, y * DeviceInfo.Instance.Height,
+ 0.44444 * DeviceInfo.Instance.Width, 0.25 * DeviceInfo.Instance.Height);
+ }
+
+ private void PickUp(MouseEventArgs mouseEventArgs)
+ {
+ if (mouseEventArgs == null)
+ {
+ return;
+ }
+
+ if (Parent is Layout parent)
+ {
+ parent.RaiseChild(this);
+ }
+
+ Opacity = 0.7;
+ thumbnailBox.Scale = 0.94;
+ edge.IsVisible = true;
+ deleteButton.IsVisible = false;
+ reorededPosition = PageIndex;
+ IsPicked = true;
+ }
+
+ /// <summary>
+ /// Called to move the thumbnail to the specified Index.
+ /// </summary>
+ /// <param name="destIndex">The index of the destination</param>
+ public void Move(int destIndex)
+ {
+ this.AbortAnimation("MoveAnimation");
+
+ new Animation(v => Move(PageIndex, destIndex, v), 0.0, 1.0).Commit(this, "MoveAnimation", 16, 200,
+ Easing.Linear,
+ (v, isCanceled) =>
+ {
+ if (isCanceled == false)
+ {
+ PageInfo.PageIndex = destIndex;
+ foreach (var widget in PageInfo.Widgets)
+ {
+ UpdateWidgetCommand.Execute(widget);
+ }
+ }
+ });
+ }
+
+ private void Move(int fromIndex, int toIndex, double v)
+ {
+ if (IsPicked == false)
+ {
+ var fromBounds = GetPixelBoundsByPageIndex(fromIndex);
+ var toBounds = GetPixelBoundsByPageIndex(toIndex);
+ TranslationX = (toBounds.X - fromBounds.X) * v;
+ TranslationY = (toBounds.Y - fromBounds.Y) * v;
+ }
+ }
+
+ private void Drag(MouseEventArgs mouseEventArgs)
+ {
+ if (IsPicked == false)
+ {
+ return;
+ }
+
+ TranslationX = mouseEventArgs.Offset.X;
+ TranslationY = mouseEventArgs.Offset.Y;
+
+ int current = GetCurrentIndexByPosition();
+ if (current < PageCount && current != reorededPosition)
+ {
+ Reordered?.Invoke(this, new ReorderedEventArgs { From = PageIndex, To = current });
+ reorededPosition = current;
+ }
+ }
+
+ private async void Drop(MouseEventArgs mouseEventArgs)
+ {
+ if (IsPicked == false)
+ {
+ return;
+ }
+
+ IsPicked = false;
+
+ Opacity = 1.0;
+ edge.IsVisible = false;
+ thumbnailBox.Scale = 1.0;
+ if (mouseEventArgs != null)
+ {
+ var bounds = GetPixelBoundsByPageIndex(PageIndex);
+ await this.TranslateTo(bounds.X - X, bounds.Y - Y, 150);
+ }
+
+ SetBounds();
+ deleteButton.IsVisible = true;
+ }
+
+ /// <summary>
+ /// Returns the thumbnail that was moving when the all page was closed to its original position.
+ /// </summary>
+ public void Return()
+ {
+ this.AbortAnimation("MoveAnimation");
+
+ if (IsPicked)
+ {
+ Drop(null);
+ }
+ }
+
+ private int GetCurrentIndexByPosition()
+ {
+ int current = PageIndex;
+ for (int page = 0; page < WidgetsInformationCenter.MaxWidgetsPage; page++)
+ {
+ var bounds = GetPixelBoundsByPageIndex(page);
+ double boundsCenterX = bounds.X + (bounds.Width / 2.0);
+ double boundsCenterY = bounds.Y + (bounds.Height / 2.0);
+ double centerX = (X + TranslationX) + (Width / 2.0);
+ double centerY = (Y + TranslationY) + (Height / 2.0);
+
+ if ((Math.Pow(centerX - boundsCenterX, 2) + Math.Pow(centerY - boundsCenterY, 2)) < Math.Pow((Width / 4.0), 2))
+ {
+ current = page;
+ break;
+ }
+ }
+
+ return current;
+ }
+
+ private void DeletePage()
+ {
+ if (PageInfo.Widgets == null || PageInfo.Widgets.Count == 0)
+ {
+ DeleteWidgetPageCommand.Execute(PageInfo);
+ return;
+ }
+
+ var dialog = new TwoButtonPopup()
+ {
+ Title = "Delete page",
+ Content = new Label()
+ {
+ Text = "This page and all the items it contains will be deleted.",
+ FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
+ Margin = new Thickness(DeviceInfo.Instance.Width * 0.04, 0, DeviceInfo.Instance.Width * 0.04, 0),
+ },
+ };
+
+ var leftButton = new Button() { Text = "Cancel" };
+ leftButton.Clicked += (s, arg) =>
+ {
+ dialog.Hide();
+ };
+ dialog.FirstButton = leftButton;
+
+ var rightButton = new Button() { Text = "Delete" };
+ rightButton.Clicked += (s, arg) =>
+ {
+ DeleteWidgetPageCommand.Execute(PageInfo);
+ dialog.Hide();
+ };
+ dialog.SecondButton = rightButton;
+
+ dialog.Show();
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == PageCountProperty.PropertyName)
+ {
+ SetBounds();
+ deleteButton.IsVisible = PageCount > 1;
+ }
+ }
+
+ private void UpdateThumbnail()
+ {
+ thumbnailBox.Children.Clear();
+
+ foreach (var widget in PageInfo.Widgets)
+ {
+ var preview = new Image { Source = widget.PreviewImagePath };
+ thumbnailBox.Children.Add(preview, widget.X, 4, widget.Y, widget.Y + (widget.Type == WidgetInformation.SizeType.SIZE_4x2 ? 2 : 4));
+ }
+ }
+
+ private void WidgetsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ case NotifyCollectionChangedAction.Remove:
+ case NotifyCollectionChangedAction.Move:
+ case NotifyCollectionChangedAction.Replace:
+ UpdateThumbnail();
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ thumbnailBox.Children.Clear();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.ViewModel;
+using System.Threading.Tasks;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for Edit state of Widgets inherited from IWidgetsState
+ /// </summary>
+ public class EditState : IWidgetsState
+ {
+ /// <summary>
+ /// You can select the options you need when changing state.
+ /// </summary>
+ public enum StateOption
+ {
+ ToHome,
+ PickedUpWidget,
+ }
+
+ /// <summary>
+ /// Property for WidgetLayout to execute some behavior regarding with change of state
+ /// </summary>
+ public IWidgetsLayout StateLayout { get; private set; }
+ /// <summary>
+ /// Property for IWidgetsStateHandler to set each state
+ /// </summary>
+ public IWidgetsStateHandler StateHandler { get; private set; }
+ public WidgetsState State => WidgetsState.Edit;
+
+ /// <summary>
+ /// This method will be called when Widgets enter Edit State
+ /// </summary>
+ /// <param name="args">null</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepIn(Enum args = null)
+ {
+ Log.Debug("EditState:StepIn called");
+
+ StateLayout.PageScroller.ReorderingStarted += GoToReorderState;
+ StateLayout.MenuButton.IsVisible = false;
+ StateLayout.AppsButton.IsVisible = false;
+ StateLayout.PageScroller.ScaleDown();
+
+ if (args is StateOption option)
+ {
+ if (option == StateOption.ToHome)
+ {
+ StateHandler.SetState(WidgetsState.Normal, NormalState.StateOption.ToHome);
+ }
+ else if (option == StateOption.PickedUpWidget)
+ {
+ StateHandler.SetState(WidgetsState.Reorder);
+ }
+ }
+
+ await Task.Delay(1);
+ }
+
+ /// <summary>
+ /// This method will be called Widgets exit from Edit State
+ /// </summary>
+ /// <param name="args">Next WidgetsState</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepOut(Enum args = null)
+ {
+ Log.Debug("EditState:StepOut called");
+ StateLayout.PageScroller.ReorderingStarted -= GoToReorderState;
+
+ await Task.Delay(1);
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed while in Edit state
+ /// </summary>
+ public void OnBack()
+ {
+ Log.Debug("EidtState:OnBack");
+ StateHandler.SetState(WidgetsState.Normal);
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed while in Edit state
+ /// </summary>
+ public void OnHome()
+ {
+ Log.Debug("EidtState:OnHome");
+ StateHandler.SetState(WidgetsState.Normal, NormalState.StateOption.ToHome);
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed while in Edit state
+ /// </summary>
+ public void OnMenu()
+ {
+
+ }
+
+ /// <summary>
+ /// Constructor for EditState
+ /// </summary>
+ /// <param name="stateHandler">WidgetsLayout to set each state</param>
+ /// <param name="widgetLayout">WidgetsLayout</param>
+ public EditState(IWidgetsStateHandler stateHandler, IWidgetsLayout widgetLayout)
+ {
+ StateHandler = stateHandler;
+ StateLayout = widgetLayout;
+ }
+
+ /// <summary>
+ /// Set Widgets state as Reorder state
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Event arguments</param>
+ private void GoToReorderState(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Reorder);
+ }
+
+ /// <summary>
+ /// Check whether the Widgets can be changed to another state or not
+ /// </summary>
+ /// <param name="next">Next state</param>
+ /// <returns>True or not</returns>
+ public bool ChangeableState(WidgetsState next)
+ {
+ if (next == WidgetsState.Normal || next == WidgetsState.Reorder)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for IndexSelectedArgs inherited from EventArgs
+ /// </summary>
+ public class IndexSelectedArgs : EventArgs
+ {
+ public string Key { get; set; }
+ }
+
+ /// <summary>
+ /// Class for FastScrollLayout inherited from AbsoluteLayout
+ /// </summary>
+ public class FastScrollLayout : AbsoluteLayout
+ {
+ /// <summary>
+ /// EventHandler which will receive IndexSelected event
+ /// </summary>
+ public EventHandler<IndexSelectedArgs> IndexSelected;
+
+ public FastScrollLayout()
+ {
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model;
+using Homescreen.ViewModel;
+using System;
+using Xamarin.Forms;
+using Homescreen.Debug;
+using System.Threading.Tasks;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for Normal state of Widgets inherited from IWidgetsState
+ /// </summary>
+ public class NormalState : IWidgetsState
+ {
+ private OptionMenuPopup popup;
+
+ /// <summary>
+ /// You can select the options you need when changing state.
+ /// </summary>
+ public enum StateOption
+ {
+ ToHome,
+ }
+
+ /// <summary>
+ /// Property for WidgetLayout to execute some behavior regarding with change of state
+ /// </summary>
+ public IWidgetsLayout StateLayout { get; private set; }
+ /// <summary>
+ /// Property for IWidgetsStateHandler to set each state
+ /// </summary>
+ public IWidgetsStateHandler StateHandler { get; private set; }
+ public WidgetsState State => WidgetsState.Normal;
+
+ /// <summary>
+ /// This method will be called when Widgets enter Normal State
+ /// </summary>
+ /// <param name="args">null</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepIn(Enum args = null)
+ {
+ Log.Debug("Home:Normal:StepIn");
+ StateLayout.MenuButton.OnClickedCommand = new Command(() =>
+ {
+ popup = new OptionMenuPopup();
+ popup.AddMenuItem("Edit", () =>
+ {
+ Log.Debug("Edit is selected");
+
+ StateHandler.SetState(WidgetsState.Edit);
+ });
+
+ popup.AddMenuItem("Add widget", () =>
+ {
+ Log.Debug("Add widget is selected");
+
+ var page = new AddWidgetPage()
+ {
+ BindingContext = (StateLayout as BindableObject).BindingContext,
+ Title = "Add Widget",
+ };
+ NavigationPage.SetHasBackButton(page, false);
+ StateLayout.PageScroller.Navigation.PushAsync(page);
+ });
+
+ popup.AddMenuItem("Change wallpaper", () =>
+ {
+ Log.Debug("Change wallpaper is selected");
+
+ WallpaperChangedNotifier.Instance.LaunchWallpaperSetting();
+ });
+
+ popup.AddMenuItem("All pages", () =>
+ {
+ Log.Debug("All pages is selected");
+
+ StateHandler.SetState(WidgetsState.Allpage);
+ });
+
+ popup.Show(StateLayout.MenuButton);
+
+ });
+
+ StateLayout.AppsButton.OnClickedCommand = new Command(() =>
+ {
+ HomeMessagingCenter.Send(this, Message.ShowApps);
+ });
+
+ StateLayout.PageScroller.IsVisible = true;
+ StateLayout.PageScroller.ScaleUp();
+ StateLayout.PageScroller.EditingStarted += GoToEditState;
+ StateLayout.PageScroller.ReorderingStarted += GoToReorderState;
+ StateLayout.MenuButton.IsVisible = true;
+ StateLayout.AppsButton.IsVisible = true;
+
+ if (args is StateOption option && option == StateOption.ToHome)
+ {
+ StateLayout.PageScroller.ScrollTo(0, false);
+ }
+
+ await Task.Delay(1);
+ }
+ /// <summary>
+ /// This method will be called Widgets exit from Normal State
+ /// </summary>
+ /// <param name="args">Next WidgetsState</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepOut(Enum args = null)
+ {
+ StateLayout.PageScroller.EditingStarted -= GoToEditState;
+ StateLayout.PageScroller.ReorderingStarted -= GoToReorderState;
+ StateLayout.MenuButton.OnClickedCommand = null;
+ StateLayout.AppsButton.OnClickedCommand = null;
+
+ popup?.Hide();
+ popup = null;
+
+ await Task.Delay(1);
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed while in Normal state
+ /// </summary>
+ public void OnBack()
+ {
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed while in Normal state
+ /// </summary>
+ public void OnHome()
+ {
+ StateLayout.PageScroller.ScrollTo(0);
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed while in Normal state
+ /// </summary>
+ public void OnMenu()
+ {
+ if (popup == null ||
+ popup.IsDismissed)
+ {
+ StateLayout.MenuButton.OnClickedCommand.Execute(string.Empty);
+ }
+ else
+ {
+ popup.Hide();
+ }
+ }
+
+ /// <summary>
+ /// Constructor for NormalState
+ /// </summary>
+ /// <param name="stateHandler">WidgetsLayout to set each state</param>
+ /// <param name="widgetLayout">WidgetsLayout</param>
+ public NormalState(IWidgetsStateHandler stateHandler, IWidgetsLayout widgetLayout)
+ {
+ StateHandler = stateHandler;
+ StateLayout = widgetLayout;
+ }
+
+ /// <summary>
+ /// Set Widgets state as Edit state
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Event arguments</param>
+ private void GoToEditState(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Edit, null);
+ }
+
+ /// <summary>
+ /// Set Widgets state as Reorder state
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Event arguments</param>
+ private void GoToReorderState(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Edit, EditState.StateOption.PickedUpWidget);
+ }
+
+ /// <summary>
+ /// Check whether the Widgets can be changed to another state or not
+ /// </summary>
+ /// <param name="next">Next state</param>
+ /// <returns>True or not</returns>
+ public bool ChangeableState(WidgetsState next)
+ {
+ if (next == WidgetsState.Edit || next == WidgetsState.Allpage)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Widgets.PreviewImage"
+ BindingContext="{x:Reference preview}"
+ WidthRequest="{Binding WidthRequest}"
+ HeightRequest="{Binding HeightRequest}">
+ <BoxView x:Name="background"
+ BindingContext="{x:Reference preview}"
+ WidthRequest="{Binding WidthRequest}"
+ HeightRequest="{Binding HeightRequest}"
+ AbsoluteLayout.LayoutBounds=".5, 0, AutoSize, AutoSize"
+ AbsoluteLayout.LayoutFlags="PositionProportional"/>
+ <view:ClickableImage x:Name="preview"
+ AbsoluteLayout.LayoutBounds=".5, 0, AutoSize, AutoSize"
+ AbsoluteLayout.LayoutFlags="PositionProportional"/>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Model;
+using Homescreen.ViewModel;
+using System;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for PreviewImage inherited from AbsoluteLayout
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class PreviewImage : AbsoluteLayout
+ {
+ /// <summary>
+ /// Property for Widget SizeType
+ /// </summary>
+ private WidgetInformation.SizeType type;
+ /// <summary>
+ /// An accessors for type property
+ /// </summary>
+ public WidgetInformation.SizeType Type
+ {
+ get => type;
+ set
+ {
+ type = value;
+ switch (type)
+ {
+ case WidgetInformation.SizeType.SIZE_4x2:
+ preview.WidthRequest = DeviceInfo.Instance.Width * 0.4;
+ preview.HeightRequest = DeviceInfo.Instance.Width * 0.2;
+ break;
+ case WidgetInformation.SizeType.SIZE_4x4:
+ preview.WidthRequest = DeviceInfo.Instance.Width * 0.4;
+ preview.HeightRequest = DeviceInfo.Instance.Width * 0.4;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public EventHandler Clicked;
+
+ public ImageSource Source { get => preview.Source; set => preview.Source = value; }
+
+ private static Color NormalColor = new Color(0.4588, 0.8, 0.8549);
+ private static Color PressecColor = new Color(1, 0.8, 1);
+
+ /// <summary>
+ /// Constructor for PreviewImage
+ /// </summary>
+ public PreviewImage()
+ {
+ InitializeComponent();
+
+ Margin = new Thickness(0, DeviceInfo.Instance.Width * 0.025, 0, DeviceInfo.Instance.Width * 0.05);
+
+ background.Color = NormalColor;
+
+ preview.Pressed += (s, e) =>
+ {
+ background.Color = PressecColor;
+ };
+
+ preview.Released += (s, e) =>
+ {
+ background.Color = NormalColor;
+ };
+
+ preview.Clicked += (s, e) =>
+ {
+ Clicked?.Invoke(this, EventArgs.Empty);
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.Threading.Tasks;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Class for Reorder state of Widgets inherited from IWidgetsState
+ /// </summary>
+ public class ReorderState : IWidgetsState
+ {
+ /// <summary>
+ /// Property for WidgetLayout to execute some behavior regarding with change of state
+ /// </summary>
+ public IWidgetsLayout StateLayout { get; private set; }
+ /// <summary>
+ /// Property for IWidgetsStateHandler to set each state
+ /// </summary>
+ public IWidgetsStateHandler StateHandler { get; private set; }
+ public WidgetsState State => WidgetsState.Reorder;
+
+ /// <summary>
+ /// This method will be called when Widgets enter Reorder State
+ /// </summary>
+ /// <param name="args">null</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepIn(Enum args = null)
+ {
+ StateLayout.PageScroller.StartReorder();
+ StateLayout.PageScroller.ReorderingStopped += StopReorder;
+
+ await Task.Delay(1);
+ }
+
+ /// <summary>
+ /// This method will be called Widgets exit from Reorder State
+ /// </summary>
+ /// <param name="args">Next WidgetsState</param>
+ /// <returns>A task instance of this method.</returns>
+ public async Task StepOut(Enum args = null)
+ {
+ StateLayout.PageScroller.ReorderingStopped -= StopReorder;
+ StateLayout.PageScroller.StopReorder();
+
+ await Task.Delay(1);
+ }
+
+ /// <summary>
+ /// This method will be called when Back key is pressed while in Reorder state
+ /// </summary>
+ public void OnBack()
+ {
+ StateHandler.SetState(WidgetsState.Edit);
+ }
+
+ /// <summary>
+ /// This method will be called when Home key is pressed while in Reorder state
+ /// </summary>
+ public void OnHome()
+ {
+ StateHandler.SetState(WidgetsState.Edit, EditState.StateOption.ToHome);
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed while in Reorder state
+ /// </summary>
+ public void OnMenu()
+ {
+ }
+
+ /// <summary>
+ /// Constructor for ReorderState
+ /// </summary>
+ /// <param name="stateHandler">WidgetsLayout to set each state</param>
+ /// <param name="widgetLayout">WidgetsLayout</param>
+ public ReorderState(IWidgetsStateHandler stateHandler, IWidgetsLayout widgetLayout)
+ {
+ StateHandler = stateHandler;
+ StateLayout = widgetLayout;
+ }
+
+ /// <summary>
+ /// This method will be called when mouse up event is occurred while in Reorder state
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Argument regarding event</param>
+ public void StopReorder(object sender, EventArgs e)
+ {
+ StateHandler.SetState(WidgetsState.Edit);
+ }
+
+ /// <summary>
+ /// Check whether the Widgets can be changed to another state or not
+ /// </summary>
+ /// <param name="next">Next state</param>
+ /// <returns>True or not</returns>
+ public bool ChangeableState(WidgetsState next)
+ {
+ if (next == WidgetsState.Edit)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Widgets.WidgetLayout"
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ DeleteWidgetCommand="{Binding DeleteWidgetCommand}"
+ ScrollToPageCommand="{Binding ScrollToPageCommand}"
+ PickedWidgetPosition="{Binding PickedWidgetPosition}">
+
+ <local:ImageButton x:Name="widgetDeleteButton"
+ NormalImage="btn_delete_nor.png"
+ PressedImage="btn_delete_press.png"
+ HorizontalOptions="Start"
+ VerticalOptions="Start"
+ IsVisible="False"
+ AbsoluteLayout.LayoutFlags="All">
+ </local:ImageButton>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.Model;
+using Homescreen.Debug;
+using System;
+using Homescreen.ViewModel;
+using Homescreen.ViewModel.Widgets;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// This class is for scrolling
+ /// </summary>
+ class ScrollTimer
+ {
+ /// <summary>
+ /// Property that represents callback() method
+ /// </summary>
+ public delegate void Callback();
+ private Callback callback;
+
+ public bool IsRunning { get; private set; }
+
+ /// <summary>
+ /// Stops scrolling
+ /// </summary>
+ public void Stop()
+ {
+ IsRunning = false;
+ }
+
+ /// <summary>
+ /// For scrolling, this method will execute callback() method what if stop() method didn't called for 1500ms
+ /// </summary>
+ public void Start()
+ {
+ IsRunning = true;
+
+ Device.StartTimer(TimeSpan.FromMilliseconds(1500), () =>
+ {
+ if (IsRunning == false)
+ {
+ return false;
+ }
+
+ callback();
+ return true;
+ });
+ }
+
+ /// <summary>
+ /// Method will be executed after scrolling
+ /// </summary>
+ /// <param name="callback">Method should be executed after scrolling</param>
+ public ScrollTimer(Callback callback)
+ {
+ this.callback = callback;
+ }
+ }
+
+ /// <summary>
+ /// This class is for WidgetLayout inherited from AbsoluteLayout and implement IMouseEventReceiver
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class WidgetLayout : AbsoluteLayout, IMouseEventReceiver
+ {
+ private WidgetInformation widgetInfo;
+ /// <summary>
+ /// Property represents WidgetInformation
+ /// </summary>
+ public WidgetInformation WidgetInfo
+ {
+ get => widgetInfo;
+ set
+ {
+ widgetInfo = value;
+
+ double x = 0.01515;
+ double y = widgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 ? 0.03401 : 0.01515;
+ double w = 0.08333;
+ double h = widgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 ? 0.16666 : 0.08333;
+ SetLayoutBounds(WidgetDeleteButton, new Rectangle(x, y, w, h));
+ }
+ }
+
+ /// <summary>
+ /// BindableProperty to make binding with DeleteWidgetCommand in WidgetInformationCenter
+ /// </summary>
+ public static readonly BindableProperty DeleteWidgetCommandProperty
+ = BindableProperty.Create("DeleteWidgetCommand", typeof(Command), typeof(WidgetLayout));
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollToPageCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(WidgetLayout));
+
+ public static readonly BindableProperty PickedWidgetPositionProperty = BindableProperty.Create(
+ "PickedWidgetPosition", typeof(Rectangle), typeof(WidgetLayout), defaultValue: Rectangle.Zero, defaultBindingMode: BindingMode.OneWayToSource);
+
+ /// <summary>
+ /// An accessors for DeleteWidgetCommand BindableProperty
+ /// </summary>
+ public Command DeleteWidgetCommand
+ {
+ get => (Command)GetValue(DeleteWidgetCommandProperty);
+ set => SetValue(DeleteWidgetCommandProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollToPageCommand
+ /// </summary>
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+
+ public Rectangle PickedWidgetPosition
+ {
+ get => (Rectangle)GetValue(PickedWidgetPositionProperty);
+ set => SetValue(PickedWidgetPositionProperty, value);
+ }
+
+ /// <summary>
+ /// Represents whether this WidgetLayout is picked or not
+ /// </summary>
+ public bool IsPicked { get; private set; }
+
+ /// <summary>
+ /// Property for WidgetDeleteButton
+ /// </summary>
+ public ImageButton WidgetDeleteButton => widgetDeleteButton;
+
+ /// <summary>
+ /// Gets the coordinate of where the picked-up widget was
+ /// </summary>
+ public Rectangle Geometry
+ {
+ get
+ {
+ VisualElement parent = (VisualElement)Parent;
+ if (parent == null)
+ {
+ return Bounds;
+ }
+
+ return new Rectangle { X = parent.X + X, Y = parent.Y + Y, Width = Width, Height = Height };
+ }
+ }
+
+ /// <summary>
+ /// This means that the position of the picked widget is on both sides of the screen.
+ /// </summary>
+ public static Rectangle EdgeArea = new Rectangle { X = double.NegativeInfinity, Y = double.NegativeInfinity, Width = 0, Height = 0 };
+
+ /// <summary>
+ /// This means that the picked widget is down.
+ /// </summary>
+ public static Rectangle DroppedArea = new Rectangle { X = double.PositiveInfinity, Y = double.PositiveInfinity, Width = 0, Height = 0 };
+
+ /// <summary>
+ /// The left coordinate of the position where the widget layout will be placed on the grid.
+ /// </summary>
+ public int Left => WidgetInfo.X;
+
+ /// <summary>
+ /// The top coordinate of the position where the widget layout will be placed on the grid.
+ /// </summary>
+ public int Top
+ {
+ get => WidgetInfo.Y;
+ set => WidgetInfo.Y = value;
+ }
+
+ /// <summary>
+ /// The right coordinate of the position where the widget layout will be placed on the grid.
+ /// </summary>
+ public int Right => Left + Columns;
+
+ /// <summary>
+ /// The bottom coordinate of the position where the widget layout will be placed on the grid.
+ /// </summary>
+ public int Bottom => Top + Rows;
+
+ /// <summary>
+ /// Relative size of widget height
+ /// </summary>
+ public int Rows => (WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 ? 2 : 4);
+
+ /// <summary>
+ /// Relative size of widget width
+ /// </summary>
+ public int Columns => 4;
+
+ /// <summary>
+ /// Page index where the widget is located.
+ /// </summary>
+ public int PageIndex
+ {
+ get => WidgetInfo.PageIndex;
+ set => WidgetInfo.PageIndex = value;
+ }
+
+ /// <summary>
+ /// Unique Id of widget
+ /// </summary>
+ public long WidgetID => WidgetInfo.Id;
+
+ private ScrollTimer scrollTimer;
+
+ /// <summary>
+ /// EventHandler for MouseDown event
+ /// MouseDown property must be declared, because WidgetLayout implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseDown { get; }
+ /// <summary>
+ /// EventHandler for MouseUp event
+ /// MouseUp property must be declared, because WidgetLayout implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseUp { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseMove event
+ /// MouseMove property must be declared, because WidgetLayout implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseMove { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseHold event
+ /// MouseHold property must be declared, because WidgetLayout implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseHold { get; private set; }
+ /// <summary>
+ /// EventHandler for MouseClick event
+ /// MouseClick property must be declared, because WidgetLayout implements IMouseEventReceiver to receive mouse event
+ /// </summary>
+ public EventHandler MouseClick { get; }
+
+ /// <summary>
+ /// EventHandler for WidgetSendCancelEventHandler behavior defined in WidgetRenderer
+ /// </summary>
+ public EventHandler WidgetSendCancelEventHandler;
+ /// <summary>
+ /// EventHandler for WidgetTouchBlockEventHandler behavior defined in WidgetRenderer
+ /// </summary>
+ public EventHandler WidgetTouchBlockEventHandler;
+ /// <summary>
+ /// EventHandler for WidgetTouchUnlockEventHandler behavior defined in WidgetRenderer
+ /// </summary>
+ public EventHandler WidgetTouchUnlockEventHandler;
+
+ /// <summary>
+ /// EventHandler which have method should be invoked when WidgetLayout is long-pressed
+ /// </summary>
+ public event EventHandler LongPressed;
+
+ /// <summary>
+ /// Constructor for WidgetLayout
+ /// In this constructor some mouse event callback methods should be defined
+ /// </summary>
+ public WidgetLayout()
+ {
+ InitializeComponent();
+ widgetDeleteButton.Clicked += (sender, e) =>
+ {
+ DeleteWidgetCommand.Execute(WidgetInfo);
+ };
+
+ MouseHold += (s, e) =>
+ {
+ IsPicked = true;
+ LongPressed?.Invoke(this, EventArgs.Empty);
+ };
+ MouseUp += (s, e) => TouchUp(e as MouseEventArgs);
+ MouseMove += (s, e) => TouchMove(e as MouseEventArgs);
+ }
+
+ /// <summary>
+ /// WidgetLayout enter Edit mode
+ /// </summary>
+ public void SetToEdit()
+ {
+ Log.Debug($"WidgetLayout : SetToEdit : {WidgetInfo.PageIndex} : {WidgetInfo.WidgetId}");
+
+ widgetDeleteButton.IsVisible = true;
+
+ WidgetTouchBlockEventHandler?.Invoke(this, EventArgs.Empty);
+ WidgetSendCancelEventHandler?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// WidgetLayout enter Normal mode
+ /// </summary>
+ public void SetToNormal()
+ {
+ Log.Debug($"WidgetLayout : SetToNormal : {WidgetInfo.PageIndex} : {WidgetInfo.WidgetId}");
+
+ widgetDeleteButton.IsVisible = false;
+ WidgetTouchUnlockEventHandler?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Make WidgetLayout as picked-up status
+ /// </summary>
+ public void SetToPickUp()
+ {
+ Log.Debug($"WidgetLayout : PickUp : {WidgetInfo.PageIndex} : {WidgetInfo.WidgetId}");
+
+ BackgroundColor = new Color(0, 0, 0, 0.1);
+ widgetDeleteButton.IsVisible = false;
+ }
+
+ /// <summary>
+ /// Put down the WidgetLayout
+ /// </summary>
+ public void SetToPutDown()
+ {
+ Log.Debug($"WidgetLayout : PutDown : {WidgetInfo.PageIndex} : {WidgetInfo.WidgetId}");
+
+ BackgroundColor = Color.Transparent;
+ widgetDeleteButton.IsVisible = true;
+
+ IsPicked = false;
+ }
+
+ /// <summary>
+ /// This method will be executed when mouse is up on picked-up status
+ /// In this method picked-up widget will be set to nearest empty area
+ /// </summary>
+ /// <param name="args">Information regarding mouse up event</param>
+ public void TouchUp(MouseEventArgs args)
+ {
+ Log.Debug("WidgetLayout:TouchUP");
+ if (IsPicked)
+ {
+ TranslationX = args.Offset.X;
+ TranslationY = args.Offset.Y;
+ }
+
+ scrollTimer?.Stop();
+ PickedWidgetPosition = DroppedArea;
+ }
+
+ /// <summary>
+ /// This method will be executed when mouse is moved on picked-up status
+ /// In this method picked-up widget will be moved to coordinate according to Offset
+ /// </summary>
+ /// <param name="args">Information regarding mouse up event</param>
+ public void TouchMove(MouseEventArgs args)
+ {
+ if (IsPicked)
+ {
+ TranslationX = args.Offset.X;
+ TranslationY = args.Offset.Y;
+
+ if (args.Current.X <= DeviceInfo.Instance.Width * 0.1)
+ {
+ if (scrollTimer == null || scrollTimer.IsRunning == false)
+ {
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { Direction = ScrollDirection.Previous });
+ scrollTimer = new ScrollTimer(() => ScrollToPageCommand?.Execute(new ScrollToCommandArgs { Direction = ScrollDirection.Previous }));
+ scrollTimer.Start();
+ }
+
+ PickedWidgetPosition = EdgeArea;
+ }
+ else if (args.Current.X >= DeviceInfo.Instance.Width * 0.9)
+ {
+ if (scrollTimer == null || scrollTimer.IsRunning == false)
+ {
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { Direction = ScrollDirection.Next });
+ scrollTimer = new ScrollTimer(() => ScrollToPageCommand?.Execute(new ScrollToCommandArgs { Direction = ScrollDirection.Next }));
+ scrollTimer.Start();
+ }
+
+ PickedWidgetPosition = EdgeArea;
+ }
+ else
+ {
+ scrollTimer?.Stop();
+ PickedWidgetPosition = new Rectangle(Geometry.X + args.Offset.X, Geometry.Y + args.Offset.Y, 4, WidgetInfo.Type == WidgetInformation.SizeType.SIZE_4x2 ? 2 : 4);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ x:Class="Homescreen.View.Widgets.WidgetPageLayout"
+ UpdateWidgetCommand ="{Binding UpdateWidgetCommand}"
+ ScrollToPageCommand="{Binding ScrollToPageCommand}">
+ <ContentView.Content>
+ <AbsoluteLayout>
+ <BoxView x:Name="widgetBoxBG"
+ Color="Black"
+ Opacity="0.0"
+ AbsoluteLayout.LayoutBounds=".5, .26, .98333333, 0.61458"
+ AbsoluteLayout.LayoutFlags="All"/>
+ <Grid x:Name="widgetBox"
+ AbsoluteLayout.LayoutBounds=".5, .26, .98333333, 0.61458"
+ AbsoluteLayout.LayoutFlags="All"
+ RowSpacing="0"
+ ColumnSpacing="0">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ </Grid>
+ <BoxView x:Name="candidateBox"
+ Color="White"
+ Opacity=".3"
+ IsVisible="False"
+ Scale=".9"
+ AbsoluteLayout.LayoutFlags="None"/>
+ </AbsoluteLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Model;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.Debug;
+using System.Collections.Specialized;
+
+using System;
+using System.Runtime.CompilerServices;
+using Homescreen.ViewModel.Widgets;
+using System.Threading.Tasks;
+using Homescreen.DataModel;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// WidgetPageLayout class inherited from ContentView
+ /// This is for each page which have several application icons in it
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class WidgetPageLayout : ContentView
+ {
+ /// <summary>
+ /// BindableProperty to make binding with PickedWidgetPosition in WidgetInformationCenter
+ /// </summary>
+ public static readonly BindableProperty PickedWidgetPositionProperty = BindableProperty.Create(
+ "PickedWidgetPosition", typeof(Rectangle), typeof(WidgetPageLayout), defaultValue: Rectangle.Zero);
+
+ /// <summary>
+ /// BindableProperty to make binding with UpdateWidgetCommand in WidgetInformationCenter
+ /// </summary>
+ public static readonly BindableProperty UpdateWidgetCommandProperty
+ = BindableProperty.Create("UpdateWidgetCommand", typeof(Command), typeof(WidgetPageLayout));
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollToPageCommand in WidgetsInformationCenter
+ /// </summary>
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(WidgetPageLayout));
+
+ /// <summary>
+ /// An accessors for PickedWidgetPosition BindableProperty
+ /// </summary>
+ public Rectangle PickedWidgetPosition
+ {
+ get => (Rectangle)GetValue(PickedWidgetPositionProperty);
+ set => SetValue(PickedWidgetPositionProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for UpdateWidgetCommand BindableProperty
+ /// </summary>
+ public Command UpdateWidgetCommand
+ {
+ get => (Command)GetValue(UpdateWidgetCommandProperty);
+ set => SetValue(UpdateWidgetCommandProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollToPageCommand
+ /// </summary>
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+
+ private int candateBoxPositionIndex = -1;
+
+ /// <summary>
+ /// Whether the candidate box is visible or not
+ /// </summary>
+ public bool IsCandidate
+ {
+ get => candidateBox.IsVisible;
+ set
+ {
+ if (value == false)
+ {
+ foreach (WidgetLayout widgetLayout in WidgetBox.Children)
+ {
+ if (widgetLayout.TranslationY != 0.0)
+ {
+ widgetLayout.TranslateTo(0.0, 0.0, 100);
+ }
+ }
+
+ candateBoxPositionIndex = -1;
+ }
+
+ candidateBox.IsVisible = value;
+ }
+ }
+
+ private WidgetPageScrollView parentScroller;
+
+ /// <summary>
+ /// Property saving information of this WidgetPageLayout
+ /// </summary>
+ private WidgetPageInformation pageInformation;
+ /// <summary>
+ /// An accessors for pageInformation property
+ /// </summary>
+ public WidgetPageInformation PageInformation
+ {
+ private get => pageInformation;
+ set
+ {
+ pageInformation = value;
+
+ pageInformation.Widgets.CollectionChanged += OnWidgetChanged;
+
+ InitPageWidgets();
+ }
+ }
+
+ /// <summary>
+ /// Represents the index of this WidgetPageLayout in WidgetPageScrollView
+ /// </summary>
+ public int PageIndex { get => PageInformation.PageIndex; }
+
+ /// <summary>
+ /// Property for access to Grid in WidgetPageLayout
+ /// </summary>
+ public Grid WidgetBox { get => widgetBox; }
+
+ /// <summary>
+ /// When the widget is picked, it shows a blurred background.
+ /// </summary>
+ public BoxView WidgetBoxBG { get => widgetBoxBG; }
+
+ /// <summary>
+ /// Property for the page's state
+ /// </summary>
+ private WidgetsState state;
+ /// <summary>
+ /// An accessors for state property
+ /// </summary>
+ public WidgetsState State
+ {
+ get => state;
+ set
+ {
+ state = value;
+ }
+ }
+
+ /// <summary>
+ /// EventHandler receives event when widget is long-pressed
+ /// </summary>
+ public event EventHandler ReorderingStarted;
+
+ /// <summary>
+ /// EventHandler receives event when widget is dropped
+ /// </summary>
+ public event EventHandler ReorderingStopped;
+
+ /// <summary>
+ /// A constructor for WidgetPageLayout
+ /// </summary>
+ /// <param name="parent">WidgetScrollView</param>
+ public WidgetPageLayout(WidgetPageScrollView parent)
+ {
+ parentScroller = parent;
+
+ Log.Debug("---Initialize Widget Page---");
+
+ InitializeComponent();
+ }
+
+ /// <summary>
+ /// Scales down WidgetPageLayout and enter EditMode
+ /// </summary>
+ public void ScaleDown()
+ {
+ Log.Debug("WidgetPageLayout : ScaleDown");
+ foreach (WidgetLayout item in WidgetBox.Children)
+ {
+ item.SetToEdit();
+ }
+
+ if (WidgetBox.Scale == 1.0)
+ {
+ WidgetBoxBG.FadeTo(0.1, 300, Easing.Linear);
+ WidgetBoxBG.ScaleTo(0.9, 300, Easing.Linear);
+ WidgetBox.ScaleTo(0.9, 300, Easing.Linear);
+ }
+ }
+
+ /// <summary>
+ /// Scales up WidgetPageLayout and enter NormalMode
+ /// </summary>
+ public void ScaleUp()
+ {
+ Log.Debug("WidgetPageLayout : ScaleUp");
+ foreach (WidgetLayout item in WidgetBox.Children)
+ {
+ item.SetToNormal();
+ }
+
+ if (WidgetBox.Scale == 0.9)
+ {
+ WidgetBox.ScaleTo(1, 300, Easing.Linear);
+ WidgetBoxBG.ScaleTo(1, 300, Easing.Linear);
+ WidgetBoxBG.FadeTo(0, 300, Easing.Linear);
+ }
+ }
+
+ /// <summary>
+ /// Picks up a widget to start reorder
+ /// </summary>
+ public void StartReorder()
+ {
+ Log.Debug("WidgetPageLayout : StartReorder");
+
+ MovePickedWidgetToParentScroller();
+
+ SetBinding(PickedWidgetPositionProperty, new Binding { Source = BindingContext, Path = "PickedWidgetPosition" });
+ }
+
+ private void MovePickedWidgetToParentScroller()
+ {
+ foreach (WidgetLayout item in WidgetBox.Children)
+ {
+ if (item.IsPicked)
+ {
+ var geometery = item.Geometry;
+ widgetBox.Children.Remove(item);
+
+ if (item.Rows == 2)
+ {
+ switch (item.Top)
+ {
+ case 0:
+ item.AnchorY = 1.0;
+ break;
+ case 2:
+ item.AnchorY = 0.0;
+ break;
+ default:
+ item.AnchorY = 0.5;
+ break;
+ }
+ }
+
+ if (widgetBox.Scale == 0.9)
+ {
+ item.Scale = 0.9;
+ }
+ else
+ {
+ item.ScaleTo(0.9, 300, Easing.Linear);
+ }
+
+ AbsoluteLayout.SetLayoutBounds(item, geometery);
+ AbsoluteLayout.SetLayoutFlags(item, AbsoluteLayoutFlags.None);
+ parentScroller.Children.Add(item);
+
+ item.SetToPickUp();
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Puts down a widget to stop reorder
+ /// </summary>
+ public async void StopReorder()
+ {
+ Log.Debug("WidgetPageLayout : StopReorder");
+
+ await PutDownPickedWidget();
+
+ foreach (WidgetLayout item in WidgetBox.Children)
+ {
+ if (item.IsPicked)
+ {
+ item.AnchorY = 0.5;
+ item.Scale = 1.0;
+ item.SetToPutDown();
+ break;
+ }
+ }
+
+ RemoveBinding(PickedWidgetPositionProperty);
+ }
+
+ private async Task PutDownPickedWidget()
+ {
+ foreach (var child in parentScroller.Children)
+ {
+ if (child is WidgetLayout pickedWidget)
+ {
+ if (pickedWidget.PageIndex == PageIndex)
+ {
+ if (parentScroller.CurrentPageIndex != PageIndex)
+ {
+ ScrollToPageCommand.Execute(new ScrollToCommandArgs { GoalPageIndex = PageIndex });
+ }
+
+ double rowHeight = WidgetBox.Height / 4.0;
+ double translateX = WidgetBox.X - pickedWidget.X;
+ double translateY = (WidgetBox.Y + rowHeight * pickedWidget.Top) - pickedWidget.Y;
+ await pickedWidget.TranslateTo(translateX, translateY, 200);
+
+ parentScroller.Children.Remove(pickedWidget);
+
+ pickedWidget.TranslationX = 0;
+ pickedWidget.TranslationY = 0;
+ pickedWidget.Scale = 1.0;
+ pickedWidget.AnchorY = 0.5;
+ WidgetBox.Children.Add(pickedWidget, pickedWidget.Left, pickedWidget.Right, pickedWidget.Top, pickedWidget.Right);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method will be called when the widget list named 'widgets' in pageInformation is changed(add, remove, replace, move, reset)
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Argument regarding event</param>
+ private void OnWidgetChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Log.Debug($"page({PageInformation.PageIndex}) :{e.Action}");
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ if (e.NewItems[0] is WidgetInformation item)
+ {
+ foreach (WidgetLayout viewItem in WidgetBox.Children)
+ {
+ if (viewItem.WidgetID == item.Id)
+ {
+ return;
+ }
+ }
+
+ AddWidget(item);
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ if (e.OldItems[0] is WidgetInformation item)
+ {
+ foreach (WidgetLayout viewItem in WidgetBox.Children)
+ {
+ if (viewItem.WidgetID == item.Id)
+ {
+ WidgetBox.Children.Remove(viewItem);
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ case NotifyCollectionChangedAction.Move:
+ case NotifyCollectionChangedAction.Reset:
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Adds all widgets in PageInformation's widget list
+ /// </summary>
+ private void InitPageWidgets()
+ {
+ Log.Debug($"Initialize WidgetPage {PageInformation.PageIndex}, has {PageInformation.Widgets.Count} widgets.");
+ foreach (WidgetInformation item in PageInformation.Widgets)
+ {
+ AddWidget(item);
+ }
+ }
+
+ /// <summary>
+ /// Creates WidgetLayout according to the widgetInfo and add it to WidgetPage
+ /// </summary>
+ /// <param name="widgetInfo">WidgetInformation</param>
+ private void AddWidget(WidgetInformation widgetInfo)
+ {
+ WidgetLayout layout = new WidgetLayout()
+ {
+ WidgetInfo = widgetInfo,
+ };
+
+ layout.LongPressed += (s, e) => ReorderingStarted?.Invoke(this, EventArgs.Empty);
+
+ WidgetBox.Children.Add(layout, widgetInfo.X, 4,
+ widgetInfo.Y, (widgetInfo.Type == WidgetInformation.SizeType.SIZE_4x4) ? (widgetInfo.Y + 4) : (widgetInfo.Y + 2));
+ }
+
+ private void CheckEmptySpace()
+ {
+ double rowHeight = WidgetBox.Height / 4;
+ Point[] startPosition = new Point[]
+ {
+ new Point { X = WidgetBox.X, Y = WidgetBox.Y },
+ new Point { X = WidgetBox.X, Y = (WidgetBox.Y + rowHeight) },
+ new Point { X = WidgetBox.X, Y = (WidgetBox.Y + rowHeight * 2) },
+ };
+
+ int posIndex = -1;
+ for (int index = 0; index < 3; index++)
+ {
+ double distance = Math.Pow(startPosition[index].X - PickedWidgetPosition.X, 2);
+ distance += Math.Pow(startPosition[index].Y - PickedWidgetPosition.Y, 2);
+
+ if (distance <= Math.Pow(rowHeight / 3.0, 2))
+ {
+ posIndex = index;
+ break;
+ }
+ }
+
+ if (posIndex == candateBoxPositionIndex)
+ {
+ return;
+ }
+
+ candateBoxPositionIndex = posIndex;
+ if (FindEmptySpace(posIndex, PickedWidgetPosition.Width, PickedWidgetPosition.Height))
+ {
+ if (PickedWidgetPosition.Height == 2)
+ {
+ candidateBox.AnchorY = (2 - posIndex) / 2.0;
+ }
+ else
+ {
+ candidateBox.AnchorY = 0.5;
+ }
+
+ AbsoluteLayout.SetLayoutBounds(candidateBox, new Rectangle
+ {
+ X = startPosition[posIndex].X,
+ Y = startPosition[posIndex].Y,
+ Width = WidgetBox.Width,
+ Height = rowHeight * PickedWidgetPosition.Height,
+ });
+ IsCandidate = true;
+ }
+ else
+ {
+ IsCandidate = false;
+ }
+ }
+
+ private bool FindEmptySpace(int posIndex, double width, double height)
+ {
+ if (posIndex < 0 || posIndex >= 3)
+ {
+ return false;
+ }
+
+ if (posIndex >= 1 && height == 4)
+ {
+ return false;
+ }
+
+ if (WidgetBox.Children.Count >= 2)
+ {
+ return false;
+ }
+
+ if (WidgetBox.Children.Count == 0 || CanPush(posIndex, width, height))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool CanPush(int posIndex, double width, double height)
+ {
+ if (WidgetBox.Children.Count != 1 || posIndex == 1 || height == 4)
+ {
+ return false;
+ }
+
+ if (WidgetBox.Children[0] is WidgetLayout widgetLayout)
+ {
+ if (widgetLayout.Rows == 4)
+ {
+ return false;
+ }
+
+ double rowHeight = WidgetBox.Height / 4.0;
+ double translateY = 0.0;
+ if (posIndex == 0)
+ {
+ translateY = rowHeight * (2 - widgetLayout.Top);
+ }
+ else if (posIndex == 2)
+ {
+ translateY = rowHeight * (-widgetLayout.Top);
+ }
+
+ if (translateY == 0)
+ {
+ return true;
+ }
+
+ widgetLayout.TranslateTo(0.0, translateY, 100);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Method called when each property that is in WidgetPageLayout has been changed
+ /// </summary>
+ /// <param name="propertyName">Name of the changed property</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == PickedWidgetPositionProperty.PropertyName)
+ {
+ if (PageIndex == parentScroller.CurrentPageIndex)
+ {
+ if (PickedWidgetPosition == WidgetLayout.EdgeArea)
+ {
+ IsCandidate = false;
+ }
+ else if (PickedWidgetPosition == WidgetLayout.DroppedArea)
+ {
+ DroppedWidget();
+ }
+ else
+ {
+ CheckEmptySpace();
+ }
+ }
+ }
+ }
+
+ private async void DroppedWidget()
+ {
+ if (IsCandidate)
+ {
+ double rowHeight = WidgetBox.Height / 4.0;
+ foreach (WidgetLayout widgetLayout in WidgetBox.Children)
+ {
+ int tranlateY = (int)((widgetLayout.TranslationY / rowHeight));
+ if (tranlateY != 0)
+ {
+ int top = widgetLayout.Top + tranlateY;
+ widgetLayout.Top = top;
+
+ UpdateWidgetCommand.Execute(widgetLayout.WidgetInfo);
+
+ widgetLayout.TranslationY = 0.0;
+ WidgetBox.Children.Add(widgetLayout, 0, 4, top, top + 2);
+ }
+ }
+
+ candidateBox.IsVisible = false;
+
+ WidgetLayout pickedWidgetLayout = null;
+ foreach (var child in parentScroller.Children)
+ {
+ if (child is WidgetLayout widget)
+ {
+ pickedWidgetLayout = widget;
+ break;
+ }
+ }
+
+ if (pickedWidgetLayout != null)
+ {
+ pickedWidgetLayout.AnchorY = candidateBox.AnchorY;
+ await pickedWidgetLayout.TranslateTo(candidateBox.X - pickedWidgetLayout.X, candidateBox.Y - pickedWidgetLayout.Y, 100);
+
+ parentScroller.Children.Remove(pickedWidgetLayout);
+
+ pickedWidgetLayout.PageIndex = PageIndex;
+ pickedWidgetLayout.Top = candateBoxPositionIndex;
+
+ pickedWidgetLayout.Scale = 1.0;
+ pickedWidgetLayout.TranslationX = 0;
+ pickedWidgetLayout.TranslationY = 0;
+ WidgetBox.Children.Add(pickedWidgetLayout, pickedWidgetLayout.Left, pickedWidgetLayout.Right,
+ pickedWidgetLayout.Top, pickedWidgetLayout.Bottom);
+
+ candateBoxPositionIndex = -1;
+
+ UpdateWidgetCommand.Execute(pickedWidgetLayout.WidgetInfo);
+ }
+ }
+
+ ReorderingStopped?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void Print()
+ {
+ foreach (WidgetLayout item in WidgetBox.Children)
+ {
+ Log.Debug($" {item.WidgetInfo.WidgetId}");
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Homescreen.View"
+ x:Class="Homescreen.View.Widgets.WidgetPageScrollView"
+ ItemsSource="{Binding WidgetsInformation}"
+ PageCount = "{Binding PageCount, Mode=OneWayToSource}"
+ ScrollPosition = "{Binding ScrollPosition, Mode=OneWayToSource}"
+ ScrollToPageCommand ="{Binding ScrollToPageCommand, Mode=OneWayToSource}">
+ <local:HomeScrollView x:Name="widgetsScrollView"
+ Orientation="Horizontal"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, .90"
+ AbsoluteLayout.LayoutFlags="All">
+ <StackLayout x:Name="widgetPageContainer"
+ Orientation="Horizontal"
+ Spacing="0"
+ Padding="0"
+ Margin="0">
+ </StackLayout>
+ </local:HomeScrollView>
+ <local:PageIndicator x:Name="indicator"
+ AbsoluteLayout.LayoutBounds=".5, .985, .47, .052"
+ AbsoluteLayout.LayoutFlags="All"/>
+</AbsoluteLayout>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.ViewModel;
+using Homescreen.ViewModel.Widgets;
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// A Constructor for WidgetPageScrollView.
+ /// In this constructor, many properties make binding with view-model named WidgetsInformationCenter.
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class WidgetPageScrollView : AbsoluteLayout
+ {
+ /// <summary>
+ /// BindableProperty to make binding with WidgetsInformation in WidgetsInformationCenter.
+ /// </summary>
+ public static readonly BindableProperty ItemsSourceProperty
+ = BindableProperty.Create("ItemsSource", typeof(ObservableCollection<WidgetPageInformation>),
+ typeof(WidgetPageScrollView), default(ObservableCollection<WidgetPageInformation>));
+
+ /// <summary>
+ /// BindableProperty to make binding with PageCount in WidgetsInformationCenter.
+ /// </summary>
+ public static readonly BindableProperty PageCountProperty = BindableProperty.Create(
+ "PageCount", typeof(int), typeof(WidgetPageScrollView), defaultValue: 0, defaultBindingMode: BindingMode.OneWayToSource);
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollPosition in WidgetsInformationCenter.
+ /// </summary>
+ public static readonly BindableProperty ScrollPositionProperty = BindableProperty.Create(
+ "ScrollPosition", typeof(double), typeof(WidgetPageScrollView), defaultValue: 0.0, defaultBindingMode: BindingMode.OneWayToSource);
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollToPage Command in WidgetsInformationCenter.
+ /// </summary>
+ public static readonly BindableProperty ScrollToPageCommandProperty
+ = BindableProperty.Create("ScrollToPageCommand", typeof(Command), typeof(WidgetPageScrollView), defaultBindingMode: BindingMode.OneWayToSource);
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollX in HomeScrollView inherited from ScrollView.
+ /// </summary>
+ public static readonly BindableProperty ScrollXProperty = BindableProperty.Create(
+ "ScrollX", typeof(double), typeof(WidgetPageScrollView), defaultValue: 0.0, defaultBindingMode: BindingMode.OneWay);
+
+ /// <summary>
+ /// BindableProperty to make binding with ScrollerSizeProperty in HomeScrollView inherited from ScrollView.
+ /// </summary>
+ public static readonly BindableProperty ScrollerSizeProperty = BindableProperty.Create(
+ "ScrollerSize", typeof(Size), typeof(WidgetPageScrollView), defaultValue: Size.Zero, defaultBindingMode: BindingMode.OneWay);
+
+ /// <summary>
+ /// An accessors for ItemsSourceProperty
+ /// </summary>
+ public ObservableCollection<WidgetPageInformation> ItemsSource
+ {
+ get { return (ObservableCollection<WidgetPageInformation>)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ /// <summary>
+ /// An accessors for PageCountProperty
+ /// </summary>
+ public int PageCount
+ {
+ get => (int)GetValue(PageCountProperty);
+ set => SetValue(PageCountProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollPositionProperty
+ /// </summary>
+ public double ScrollPosition
+ {
+ get => (double)GetValue(ScrollPositionProperty);
+ set => SetValue(ScrollPositionProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollXProperty
+ /// </summary>
+ public double ScrollX
+ {
+ get => (double)GetValue(ScrollXProperty);
+ set => SetValue(ScrollXProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollerSizeProperty
+ /// </summary>
+ public Size ScrollerSize
+ {
+ get => (Size)GetValue(ScrollerSizeProperty);
+ set => SetValue(ScrollerSizeProperty, value);
+ }
+
+ /// <summary>
+ /// An accessors for ScrollToPageCommandProperty
+ /// </summary>
+ public Command ScrollToPageCommand
+ {
+ get => (Command)GetValue(ScrollToPageCommandProperty);
+ set => SetValue(ScrollToPageCommandProperty, value);
+ }
+
+ /// <summary>
+ /// A property indicates current page index
+ /// </summary>
+ public int CurrentPageIndex => ((int)(ScrollPosition * PageCount + 0.5));
+
+ /// <summary>
+ /// An accessors for PageIndicator
+ /// </summary>
+ public PageIndicator PageIndicator { get => indicator; }
+ /// <summary>
+ /// An accessors for grid in WidgetPageScrollView
+ /// </summary>
+ public StackLayout WidgetPageContainer { get => widgetPageContainer; }
+ /// <summary>
+ /// An accessors for HomeScrollView named widgetsScrollView
+ /// </summary>
+ public HomeScrollView WidgetsScrollView { get => widgetsScrollView; }
+
+ /// <summary>
+ /// EventHandler which have method should be invoked when Widgets are long-pressed
+ /// </summary>
+ public event EventHandler ReorderingStarted;
+ /// <summary>
+ /// EventHandler which have method should be invoked when WidgetsPageScrollView is long-pressed
+ /// </summary>
+ public event EventHandler EditingStarted;
+
+ //State handler
+ public event EventHandler ReorderingStopped;
+
+ /// <summary>
+ /// A Constructor of WidgetPageScrollView
+ /// Add necessary EventHandlers and make Binding for some properties that are needed to control the ScrollView
+ /// </summary>
+ public WidgetPageScrollView()
+ {
+ Log.Debug("---Initialize WidgetPageScrollView---");
+ InitializeComponent();
+
+ SetBinding(ScrollXProperty, new Binding { Source = widgetsScrollView, Path = "ScrollX" });
+ SetBinding(ScrollerSizeProperty, new Binding { Source = widgetsScrollView, Path = "ContentSize" });
+
+ widgetsScrollView.LongPressed += (s, e) => EditingStarted?.Invoke(this, EventArgs.Empty);
+
+ ScrollToPageCommand = new Command<ScrollToCommandArgs>((args) =>
+ {
+ switch (args.Direction)
+ {
+ case ScrollDirection.First:
+ ScrollTo(0);
+ break;
+ case ScrollDirection.Previous:
+ ScrollTo(CurrentPageIndex - 1);
+ break;
+ case ScrollDirection.Next:
+ ScrollTo(CurrentPageIndex + 1);
+ break;
+ case ScrollDirection.Last:
+ ScrollTo(PageCount - 1);
+ break;
+ case ScrollDirection.Directly:
+ default:
+ ScrollTo(args.GoalPageIndex);
+ break;
+ }
+ });
+ indicator.Clicked += (indicator, clickedArgs) =>
+ {
+ var args = clickedArgs as IndicatorClickedEventArgs;
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { GoalPageIndex = args.ClickedPage });
+ };
+ }
+
+ /// <summary>
+ /// Add all pages in ItemsSource collection to the WidgetPageScrollView
+ /// </summary>
+ public void SetWidgetPages()
+ {
+ Log.Debug("---Set Widget Pages---");
+ foreach (var page in ItemsSource)
+ {
+ WidgetPageAdd(page, WidgetsState.Normal);
+ }
+ }
+
+ /// <summary>
+ /// Scale down all WidgetPageLayouts in the WidgetPageScrollView
+ /// </summary>
+ public void ScaleDown()
+ {
+ foreach (WidgetPageLayout item in WidgetPageContainer.Children)
+ {
+ item.ScaleDown();
+ }
+ }
+
+ /// <summary>
+ /// Scale up all WidgetPageLayouts in the WidgetPageScrollView
+ /// </summary>
+ public void ScaleUp()
+ {
+ foreach (WidgetPageLayout item in WidgetPageContainer.Children)
+ {
+ item.ScaleUp();
+ }
+ }
+
+ /// <summary>
+ /// Call StartReorder method of each WidgetPageLayout in the WidgetPageScrollView
+ /// Make ScrollView can't be scrolled by invoking HomeScrollViewLockEventHandler
+ /// </summary>
+ public void StartReorder()
+ {
+ Log.Debug("WidgetPageScrollView : StartReorder");
+ foreach (WidgetPageLayout item in WidgetPageContainer.Children)
+ {
+ item.StartReorder();
+ }
+
+ widgetsScrollView.HomeScrollViewScrollLockEventHandler?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Call StopReorder method of each WidgetPageLayout in the WidgetPageScrollView
+ /// Make ScrollView can be scrolled by invoking HomeScrollViewUnLockEventHandler
+ /// </summary>
+ public void StopReorder()
+ {
+ Log.Debug("WidgetPageScrollView : StopReorder");
+ foreach (WidgetPageLayout item in WidgetPageContainer.Children)
+ {
+ item.StopReorder();
+ }
+
+ widgetsScrollView.HomeScrollViewScrollUnLockEventHandler?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Make property named IsVisible of PageIndicator as true
+ /// </summary>
+ public void ShowIndicator()
+ {
+ indicator.IsVisible = true;
+ }
+
+ /// <summary>
+ /// Make property named IsVisible of PageIndicator as false
+ /// </summary>
+ public void HideIndicator()
+ {
+ indicator.IsVisible = false;
+ }
+
+ /// <summary>
+ /// Scroll the WidgetPageScrollView to index of pages
+ /// </summary>
+ /// <param name="index">Index of destination page</param>
+ /// <param name="animation">If true scroll with animation or not</param>
+ public void ScrollTo(int index, bool animation = true)
+ {
+ Log.Debug($"scroll to {index} / {PageCount - 1}");
+ if (index < 0)
+ {
+ index = 0;
+ }
+ else if (index >= PageCount)
+ {
+ index = PageCount - 1;
+ }
+
+ widgetsScrollView.ScrollToAsync(index * widgetsScrollView.Width, 0, animation);
+ }
+
+ /// <summary>
+ /// Get current page from the WidgetPageScrollView
+ /// </summary>
+ /// <returns>Current Page</returns>
+ public WidgetPageLayout GetCurrentPage()
+ {
+ foreach (WidgetPageLayout item in WidgetPageContainer.Children)
+ {
+ if (item.PageIndex == CurrentPageIndex)
+ {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Method called when each property that is in WidgetPageScrollView has been changed
+ /// </summary>
+ /// <param name="propertyName">Name of the changed property</param>
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (propertyName == ItemsSourceProperty.PropertyName)
+ {
+ Log.Debug($"ItemSource was set. ItemsSource Count is {ItemsSource.Count}");
+ SetWidgetPages();
+ ItemsSource.CollectionChanged += ItemsSourceCollectionChanged;
+ }
+ else if (propertyName == ScrollerSizeProperty.PropertyName || propertyName == ScrollXProperty.PropertyName)
+ {
+ PageCount = (int)(ScrollerSize.Width / widgetsScrollView.Width);
+ ScrollPosition = ScrollX / ScrollerSize.Width;
+ }
+ }
+
+ /// <summary>
+ /// Makes WidgetLayout with pageInformation and add it to the WidgetPageScrollView
+ /// </summary>
+ /// <param name="pageInformation">Information of the page will be created</param>
+ /// <param name="state">State in which the page is created</param>
+ private void WidgetPageAdd(WidgetPageInformation pageInformation, WidgetsState state)
+ {
+ var page = new WidgetPageLayout(this)
+ {
+ PageInformation = pageInformation,
+ WidthRequest = DeviceInfo.Instance.Width,
+ State = state,
+ };
+
+ page.ReorderingStarted += (s, e) => ReorderingStarted?.Invoke(this, EventArgs.Empty);
+ page.ReorderingStopped += (s, e) => ReorderingStopped?.Invoke(this, EventArgs.Empty);
+
+ WidgetPageContainer.Children.Add(page);
+
+ Log.Debug($"Page {pageInformation.PageIndex} was added.");
+ }
+
+ /// <summary>
+ /// Removes the WidgetPage that is represented by pageInformation
+ /// </summary>
+ /// <param name="pageInformation">Information of the WidgetPage will be removed</param>
+ private void WidgetPageRemove(WidgetPageInformation pageInformation)
+ {
+ Log.Debug($"WidgetPageRemove : {pageInformation.PageIndex}");
+
+ foreach (var item in WidgetPageContainer.Children)
+ {
+ if (item is WidgetPageLayout page)
+ {
+ if (page.PageIndex == pageInformation.PageIndex)
+ {
+ WidgetPageContainer.Children.Remove(page);
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Method should be called when each member of the ItemsSourceCollection is added, removed, changed, moved or the entire list is refreshed
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="e">Arguments regarding with the events</param>
+ private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Log.Debug($"WidgetPageScrollView {e.Action} ");
+
+ /*
+ Add = 0,
+ Remove = 1,
+ Replace = 2,
+ Move = 3,
+ Reset = 4
+ */
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ Log.Debug("ItemsSourceCollectionChanged : Add called");
+ var index = e.NewStartingIndex;
+
+ foreach (var item in e.NewItems)
+ {
+ if (item is WidgetPageInformation pageInformation)
+ {
+ /* TODO: Check the state */
+ WidgetPageAdd(item as WidgetPageInformation, WidgetsState.Normal);
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ Log.Debug("ItemsSourceCollectionChanged : Remove called");
+ for (int i = e.OldItems.Count - 1; i >= 0; i--)
+ {
+ Log.Debug($"ItemsSourceCollectionChanged : page [{i}]");
+ WidgetPageRemove(e.OldItems[i] as WidgetPageInformation);
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ {
+ Log.Debug("ItemsSourceCollectionChanged : Replace called");
+ var index = e.OldStartingIndex;
+
+ foreach (var item in e.NewItems)
+ {
+ if (item is WidgetPageInformation pageInformation)
+ {
+ WidgetPageContainer.Children.RemoveAt(index++);
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ {
+ Log.Debug("ItemsSourceCollectionChanged : Move called");
+ int from = e.OldStartingIndex;
+ int to = e.NewStartingIndex;
+
+ var page = WidgetPageContainer.Children[from];
+ WidgetPageContainer.Children.Remove(page);
+ WidgetPageContainer.Children.Insert(to, page);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ {
+ Log.Debug("ItemsSourceCollectionChanged : Reset called");
+ WidgetPageContainer.Children.Clear();
+ }
+
+ break;
+ }
+ }
+
+ public void Print()
+ {
+ Log.Debug("============================================");
+ Log.Debug("ItemSource");
+ foreach (WidgetPageInformation item in ItemsSource)
+ {
+ Log.Debug($"page : {item.PageIndex}, item count : {item.Widgets.Count})");
+ foreach (WidgetInformation widget in item.Widgets)
+ {
+ Log.Debug($" widget({widget.WidgetId})");
+ }
+ }
+
+ Log.Debug("Layout");
+ foreach (WidgetPageLayout page in WidgetPageContainer.Children)
+ {
+ Log.Debug($"page index : {page.PageIndex}");
+ page.Print();
+ }
+
+ Log.Debug("============================================");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:view="clr-namespace:Homescreen.View"
+ xmlns:widgets="clr-namespace:Homescreen.View.Widgets"
+ x:Class="Homescreen.View.Widgets.WidgetsLayout">
+ <ContentView.Content>
+ <RelativeLayout
+ x:Name="Container">
+ <widgets:WidgetPageScrollView
+ x:Name="widgetsScrollView"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+
+ <view:OptionButton
+ x:Name="menuButton"
+ IconImageSource="home_button_menu.png"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.04444444, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.928125, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.18055555, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.059375, Constant=0}"/>
+
+ <view:OptionButton
+ x:Name="appsButton"
+ IconImageSource="home_button_apps.png"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.775, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.928125, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.18055555, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.059375, Constant=0}"/>
+
+ <widgets:AllpageLayout
+ x:Name="allpageLayout"
+ IsVisible="False"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"/>
+ </RelativeLayout>
+ </ContentView.Content>
+</ContentView>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.ViewModel;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Homescreen.View.Interface;
+using Homescreen.Debug;
+using System;
+
+namespace Homescreen.View.Widgets
+{
+ /// <summary>
+ /// Widgets's base layout for PageScroller, MenuButton and AppsButton
+ /// </summary>
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class WidgetsLayout : ContentView, IVisibilityChanged, IWidgetsLayout, IWidgetsStateHandler
+ {
+ /// <summary>
+ /// Button for Menu.
+ /// </summary>
+ public OptionButton MenuButton => menuButton;
+ /// <summary>
+ /// Button for Apps.
+ /// </summary>
+ public OptionButton AppsButton => appsButton;
+ /// <summary>
+ /// Property which represents WidgetsPageScroller.
+ /// </summary>
+ public WidgetPageScrollView PageScroller => widgetsScrollView;
+ public AllpageLayout AllpageLayout => allpageLayout;
+
+ /// <summary>
+ /// States property have each state such as Normal, Edit, AllPages, Reorder.
+ /// And WidgetsLayout can change each state of WidgetsLayout using this States object.
+ /// </summary>
+ private IDictionary<WidgetsState, IWidgetsState> States = new Dictionary<WidgetsState, IWidgetsState>();
+ /// <summary>
+ /// CurrentState indicates what is the current state and WidgetsLayout change the state according to this CurrentState
+ /// </summary>
+ public IWidgetsState CurrentState { get; private set; }
+
+ /// <summary>
+ /// A constructor of the WidgetsLayout which initialize xaml file for WidgetsLayout and add each state to States property.
+ /// </summary>
+ public WidgetsLayout()
+ {
+ Log.Debug("---Initialize WidgetsLayout---");
+ InitializeComponent();
+
+ BindingContext = new WidgetsInformationCenter();
+
+ States.Add(WidgetsState.Normal, CurrentState = new NormalState(this, this));
+ States.Add(WidgetsState.Edit, new EditState(this, this));
+ States.Add(WidgetsState.Reorder, new ReorderState(this, this));
+ States.Add(WidgetsState.Allpage, new AllpageState(this, this));
+
+ CurrentState.StepIn();
+ }
+
+ /// <summary>
+ /// Hide WidgetsLayout and PageIndicator with animation.
+ /// </summary>
+ /// <param name="animation">If true, animation will be continued or not</param>
+ public void Hide(bool animation = true)
+ {
+ if (CurrentState.State != WidgetsState.Normal)
+ {
+ SetState(WidgetsState.Normal);
+ }
+
+ PageScroller.HideIndicator();
+
+ Action<double, bool> finished = (v, c) =>
+ {
+ IsVisible = false;
+ };
+
+ if (animation)
+ {
+ new Animation
+ {
+ { 0, 0.8, new Animation(v => PageScroller.Opacity = v, 1, 0, Easing.CubicInOut) },
+ { 0, 0.8, new Animation(v => PageScroller.Scale = v, 1, 0.8, Easing.CubicInOut) },
+ { 0, 0.8, new Animation(v => PageScroller.TranslationY = v, 0, -(Height * 0.15), Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => MenuButton.Scale = v, 1, 0.5, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => AppsButton.Scale = v, 1, 0.5, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => MenuButton.Opacity = v, 1, 0, Easing.CubicInOut) },
+ { 0, 0.2, new Animation(v => AppsButton.Opacity = v, 1, 0, Easing.CubicInOut) },
+ }.Commit(this, "HideAppsLayout", 16, 300, null, finished);
+ }
+ else
+ {
+ finished(0, false);
+ }
+ }
+
+ /// <summary>
+ /// Show WidgetsLayout and PageIndicator with animation.
+ /// </summary>
+ /// <param name="animation">If true, animation will be continued</param>
+ public void Show(bool animation = true)
+ {
+ if (CurrentState.State != WidgetsState.Normal)
+ {
+ SetState(WidgetsState.Normal);
+ }
+
+ IsVisible = true;
+
+ Action<double, bool> finished = (v, c) =>
+ {
+ PageScroller.ShowIndicator();
+ };
+
+ if (animation)
+ {
+ new Animation
+ {
+ { 0.1, 1, new Animation(v => PageScroller.Opacity = v, 0, 1, Easing.SinInOut) },
+ { 0.1, 1, new Animation(v => PageScroller.Scale = v, 0.8, 1, Easing.SinInOut) },
+ { 0.1, 1, new Animation(v => PageScroller.TranslationY = v, -(Height * 0.15), 0, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => MenuButton.Scale = v, 0.5, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => AppsButton.Scale = v, 0.5, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => MenuButton.Opacity = v, 0, 1, Easing.SinInOut) },
+ { 0.8, 1, new Animation(v => AppsButton.Opacity = v, 0, 1, Easing.SinInOut) },
+ }.Commit(this, "ShowWidgetsLayout", 16, 300, null, finished);
+ }
+ else
+ {
+ finished(0, false);
+ }
+ }
+
+ /// <summary>
+ /// Method should be executed when BackKey is pressed.
+ /// </summary>
+ public void OnBackKeyPressed()
+ {
+ Log.Debug("WidgetsLayout.Back");
+ CurrentState.OnBack();
+ }
+
+ /// <summary>
+ /// Method should be executed when HomeKey is pressed.
+ /// </summary>
+ public void OnHomeKeyPressed()
+ {
+ Log.Debug("WidgetsLayout.Home");
+ CurrentState.OnHome();
+ }
+
+ /// <summary>
+ /// This method will be called when Menu key is pressed
+ /// </summary>
+ public void OnMenuKeyPressed()
+ {
+ Log.Debug("WidgetsLayout.Menu");
+ CurrentState.OnMenu();
+ }
+
+ /// <summary>
+ /// SetState method change Current state as State and set State to CurrentState.
+ /// </summary>
+ /// <param name="state">Next state</param>
+ /// <param name="args">Option used when change state</param>
+ public async void SetState(WidgetsState state, Enum args = null)
+ {
+ if (CurrentState == null)
+ {
+ Log.Error("CurrentState is NULL");
+ return;
+ }
+
+ if (CurrentState.ChangeableState(state) == false)
+ {
+ Log.Error($"{CurrentState.State} to {state} can not be changed.");
+ return;
+ }
+
+ if (States.TryGetValue(state, out IWidgetsState nextState))
+ {
+ Log.Debug($"Widgets State Changed : Current is {CurrentState.State}, Next is {nextState}");
+
+ await CurrentState?.StepOut(args);
+ CurrentState = nextState;
+ await CurrentState.StepIn(args);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.Model;
+using System;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using System.Linq;
+using System.Windows.Input;
+using Homescreen.Model.Interface;
+using System.Collections.Generic;
+using Homescreen.View.Apps;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// Apps page status
+ /// </summary>
+ public enum AppsState
+ {
+ Normal,
+ Edit,
+ Choose,
+ Drag,
+ }
+
+ /// <summary>
+ /// AppsInformationCeter is a view model of the Apps page layout.
+ /// Every single behavior regarding apps, folders is handled by this class.
+ /// </summary>
+ public class AppsInformationCenter : ViewModelBase
+ {
+ private event EventHandler<PackageUpdateEventArgs> PackageManagerEventHandler;
+
+ private ObservableCollection<ItemInformation> appsInformation = new ObservableCollection<ItemInformation>();
+ /// <summary>
+ /// AppsInformation property which will be realized as an Item.
+ /// </summary>
+ public ObservableCollection<ItemInformation> AppsInformation
+ {
+ get => appsInformation;
+ set
+ {
+ appsInformation = value;
+ OnPropertyChanged();
+ }
+ }
+
+ /// <summary>
+ /// ChoosedAppsInformation which has selected apps information during Choose mode.
+ /// </summary>
+ public ICollection<ItemInformation> ChoosedAppsInformation { get; set; }
+ = new List<ItemInformation>();
+
+ /// <summary>
+ /// A command property for launching an app.
+ /// </summary>
+ public ICommand LaunchAppCommand { get; set; }
+
+ /// <summary>
+ /// A command property for deleting an app or a folder.
+ /// </summary>
+ public ICommand DeleteItemCommand { get; set; }
+
+ /// <summary>
+ /// A command property for selecting an app.
+ /// </summary>
+ public ICommand ChooseAppCommand { get; set; }
+
+ /// <summary>
+ /// A command property will be executed if the Choose mode is finished.
+ /// </summary>
+ public ICommand ChooseDoneCommand { get; set; }
+
+ /// <summary>
+ /// A command property will be executed if the Choose mode is canceled.
+ /// </summary>
+ public ICommand ChooseCancelCommand { get; set; }
+
+ /// <summary>
+ /// A command property will be executed when the Choose mode being started.
+ /// </summary>
+ public ICommand ChooseStartCommand { get; set; }
+
+ /// <summary>
+ /// A command property will be executed if the folder title is changed.
+ /// </summary>
+ public ICommand FolderTextChangedCommand { get; set; }
+
+ /// <summary>
+ /// A command property will be executed if the application moves the folder inside.
+ /// </summary>
+ public Command MoveFolderInsideCommand => new Command<ItemInformation>((itemInfo) => MoveAppToFolder(itemInfo));
+
+ /// <summary>
+ /// A command property will be executed if the application moves out of the folder.
+ /// </summary>
+ public Command MoveFolderOutCommand => new Command<ItemInformation>((itemInfo) => MoveAppOutFolder(itemInfo));
+
+
+ private Command scrollToCommand;
+ public Command ScrollToCommand
+ {
+ get => scrollToCommand;
+ set
+ {
+ scrollToCommand = value;
+ }
+ }
+
+
+ private AppsState appsState;
+ /// <summary>
+ /// An Apps page's current state
+ /// </summary>
+ public AppsState AppsState
+ {
+ get => appsState;
+ set
+ {
+ appsState = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private bool isFolderVisible = false;
+ /// <summary>
+ /// A showing status of the folder on the Apps Page.
+ /// </summary>
+ public bool IsFolderVisible
+ {
+ get => isFolderVisible;
+ set
+ {
+ isFolderVisible = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private FolderInformation openedFolderInformation;
+ /// <summary>
+ /// A folder information which is currently opened on the Apps page layout.
+ /// </summary>
+ public FolderInformation OpenedFolderInformation
+ {
+ get => openedFolderInformation;
+ set
+ {
+ openedFolderInformation = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private FolderInformation choosedFolderInformation;
+ /// <summary>
+ /// A folder information which is currently selected in Choose mode.
+ /// This information can be different with OpenedFolderInformation
+ /// if user selecting apps in other folders during in Choose mode.
+ /// </summary>
+ public FolderInformation ChoosedFolderInformation
+ {
+ get => choosedFolderInformation;
+ set
+ {
+ choosedFolderInformation = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private long updatedItemId;
+ /// <summary>
+ /// An id number of updated item.
+ /// </summary>
+ public long UpdatedItemId
+ {
+ get => updatedItemId;
+ set
+ {
+
+ updatedItemId = value;
+ OnPropertyChanged("UpdatedItemId");
+ }
+ }
+
+ private int pageCount;
+ /// <summary>
+ /// A page count of apps drawer in Apps page layout
+ /// </summary>
+ public int PageCount
+ {
+ get => pageCount;
+ set
+ {
+ pageCount = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private double scrollPosition;
+ /// <summary>
+ /// A scroll position which make the Apps Page layout move to the set page position.
+ /// </summary>
+ public double ScrollPosition
+ {
+ get => scrollPosition;
+ set
+ {
+ scrollPosition = value;
+ OnPropertyChanged();
+ }
+ }
+
+ /// <summary>
+ /// A constructor of AppsInformationCenter.
+ /// </summary>
+ public AppsInformationCenter()
+ {
+ Log.Debug("AppsInformationCenter");
+
+ HomeMessagingCenter.Subscribe(this, Message.CreateFolder, (sender) =>
+ {
+ Log.Debug("CreateFolder");
+ ItemInformation newFolder = CreateFolder();
+ newFolder.Index = AppsInformation.Count;
+ AppsInformation.Add(newFolder);
+ Sort();
+
+ if (newFolder is FolderInformation folderInformation)
+ {
+ OpenedFolderInformation = folderInformation;
+ IsFolderVisible = true;
+ }
+ });
+
+ LaunchAppCommand = new Command<ItemInformation>((information) =>
+ {
+ Log.Debug($"::LaunchAppCommand called: {information.Label}");
+ if (information is AppInformation appInformation)
+ {
+ DependencyService.Get<IAppLauncher>().LaunchApp(appInformation.AppId);
+ }
+ });
+
+ DeleteItemCommand = new Command<ItemInformation>((information) =>
+ {
+ if (information is AppInformation appInformation)
+ {
+ PackageChangedNotifier.Instance.RequestUninstall(appInformation.PackageId);
+ }
+ else if (information is FolderInformation folderInformation)
+ {
+ foreach (var inApp in folderInformation.AppList)
+ {
+ inApp.ParentId = AppInformation.InitialId;
+ AppsDataProvider.Instance.Update(inApp);
+ inApp.Index = AppsInformation.Count;
+ AppsInformation.Add(inApp);
+ }
+
+ AppsDataProvider.Instance.Remove(folderInformation);
+ AppsInformation.Remove(folderInformation);
+
+ Sort();
+ }
+ });
+
+ ChooseAppCommand = new Command<ItemInformation>((information) =>
+ {
+ Log.Debug($"::ChooseAppCommand called: {information.Label}");
+ if (information.IsChecked)
+ {
+ ChoosedAppsInformation.Add(information);
+ }
+ else
+ {
+ ChoosedAppsInformation.Remove(information);
+ }
+
+ ChoosedAppsInformation = new List<ItemInformation>(ChoosedAppsInformation);
+ OnPropertyChanged("ChoosedAppsInformation");
+ });
+
+ ChooseCancelCommand = new Command(() =>
+ {
+ ChoosedFolderInformation = new FolderInformation()
+ {
+ Id = ItemInformation.InitialId,
+ };
+ ChoosedAppsInformation.Clear();
+ });
+
+ ChooseDoneCommand = new Command(() =>
+ {
+ Log.Debug($"ChoosedFolderInformation id = {ChoosedFolderInformation.Id}");
+ int count = 0;
+ foreach (var choosed in ChoosedAppsInformation)
+ {
+ if (choosed is AppInformation appInfo)
+ {
+ if (appInfo.ParentId != ItemInformation.InitialId)
+ {
+ foreach (var item in AppsInformation)
+ {
+ if ((item as ItemInformation).Id == appInfo.ParentId &&
+ item is FolderInformation)
+ {
+ (item as FolderInformation).RemoveApp(appInfo.Id);
+ (item as FolderInformation).HaveToUpdate = true;
+ }
+ }
+
+ }
+
+ appInfo.ParentId = ChoosedFolderInformation.Id;
+ ChoosedFolderInformation.AddApp(appInfo);
+ AppsInformation.Remove(appInfo);
+ }
+ else
+ {
+ Log.Error($"UnSupported type({choosed}), {choosed.Id}");
+ }
+
+ AppsDataProvider.Instance.Update(choosed);
+ Log.Debug($"({++count}) {choosed.Label} is updated.");
+ }
+
+ UpdatedItemId = ChoosedFolderInformation.Id;
+ Sort();
+
+ // Init
+ OpenedFolderInformation = ChoosedFolderInformation;
+ ChoosedFolderInformation = new FolderInformation()
+ {
+ Id = ItemInformation.InitialId,
+ };
+ ChoosedAppsInformation.Clear();
+ });
+
+ ChooseStartCommand = new Command<FolderInformation>((information) =>
+ {
+ Log.Debug($"ChooseStartCommand is called({information.Id})");
+ if (information is FolderInformation folderInfo)
+ {
+ Log.Debug($"ChoosedFolderInformation is changed({folderInfo.AppCount})");
+ AppsState = AppsState.Choose;
+ ChoosedFolderInformation = folderInfo;
+ ChoosedAppsInformation.Clear();
+ }
+ });
+
+ FolderTextChangedCommand = new Command<string>((changedText) =>
+ {
+ Log.Debug($"Folder text Changed {OpenedFolderInformation.Id}, {changedText}");
+ OpenedFolderInformation.Label = changedText;
+ for (int i = 0; i < AppsInformation.Count; i++)
+ {
+ if (AppsInformation[i] is FolderInformation appInfo)
+ {
+ if (appInfo.Id == OpenedFolderInformation.Id)
+ {
+ appInfo.Label = changedText;
+ break;
+ }
+ }
+ }
+
+ AppsDataProvider.Instance.Update(OpenedFolderInformation);
+ OpenedFolderInformation.HaveToUpdate = true;
+ UpdatedItemId = OpenedFolderInformation.Id;
+ Sort();
+ ScrollToCommand?.Execute(OpenedFolderInformation.Index / AppsPageLayout.MaxNumberOfApps);
+ });
+
+ PackageManagerEventHandler = new EventHandler<PackageUpdateEventArgs>(async (sender, arg) =>
+ {
+ PackageUpdateEventArgs e = arg;
+
+ Log.Debug($"Package is {e.PackageId} type is {e.Type}");
+ switch (e.Type)
+ {
+ case PackageUpdateType.Uninstalled:
+ for (int i = 0; i < AppsInformation.Count; i++)
+ {
+ if (AppsInformation[i] is AppInformation appInfo)
+ {
+ if (appInfo.PackageId == e.PackageId)
+ {
+ AppsDataProvider.Instance.Remove(appInfo);
+ AppsInformation.RemoveAt(i);
+ i--;
+ }
+ }
+ else if (AppsInformation[i] is FolderInformation folderInfo)
+ {
+ List<AppInformation> list = folderInfo.AppList as List<AppInformation>;
+ for (int j = 0; j < list.Count; j++)
+ {
+ if (list[j] is AppInformation info)
+ {
+ if (info.PackageId == e.PackageId)
+ {
+ AppsDataProvider.Instance.Remove(info);
+ list.RemoveAt(j);
+ j--;
+ }
+ }
+ }
+
+ folderInfo.UpdateBadgeCount();
+ UpdatedItemId = folderInfo.Id;
+ }
+ }
+
+ Sort();
+
+ break;
+ case PackageUpdateType.Installed:
+ {
+ AppInformation newItemInfo = null;
+ var installedAppList = await PackageChangedNotifier.Instance.GetAllInstalledAppInformation();
+ foreach (InstalledAppInformation installedapp in installedAppList)
+ {
+ if (installedapp.PackageId == e.PackageId)
+ {
+ InstalledAppInformation installedAppInfo = PackageChangedNotifier.Instance.GetInstalledAppInformation(installedapp.AppId);
+
+ newItemInfo = new AppInformation()
+ {
+ Label = installedAppInfo.Title,
+ IconPath = installedAppInfo.IconPath,
+ AppId = installedAppInfo.AppId,
+ PackageId = installedAppInfo.PackageId,
+ IsRemovable = installedAppInfo.IsRemovable,
+ };
+ newItemInfo.Index = AppsInformation.Count;
+ AppsInformation.Add(newItemInfo);
+ newItemInfo.Id = AppsDataProvider.Instance.Set(newItemInfo);
+ }
+ }
+
+ Sort();
+ if (newItemInfo != null)
+ {
+ ScrollToCommand?.Execute(newItemInfo.Index / AppsPageLayout.MaxNumberOfApps);
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ });
+ PackageChangedNotifier.Instance.Register(PackageManagerEventHandler);
+
+ BadgeEventNotifier.Instance.Register(new EventHandler<BadgeUpdateEventArgs>((sender, arg) =>
+ {
+ if (arg is BadgeUpdateEventArgs badgeinfo &&
+ badgeinfo.UpdatedInformation.IsVisible)
+ {
+
+ for (int i = 0; i < AppsInformation.Count; i++)
+ {
+ if (AppsInformation[i] is AppInformation appInfo)
+ {
+ if (appInfo.AppId == badgeinfo.UpdatedInformation.AppId)
+ {
+ appInfo.BadgeCount = badgeinfo.UpdatedInformation.Count;
+ UpdatedItemId = appInfo.Id;
+ return;
+ }
+ }
+ else if (AppsInformation[i] is FolderInformation folderInfo)
+ {
+ foreach (var inApp in folderInfo.AppList)
+ {
+ if (inApp is AppInformation info &&
+ inApp.AppId == badgeinfo.UpdatedInformation.AppId)
+ {
+ inApp.BadgeCount = badgeinfo.UpdatedInformation.Count;
+ folderInfo.UpdateBadgeCount();
+ UpdatedItemId = folderInfo.Id;
+ return;
+ }
+ }
+ }
+ }
+ }
+ }));
+
+ SetAppsInformation();
+
+ }
+
+ private ItemInformation CreateFolder()
+ {
+ Log.Debug("CreateFolder");
+
+ ItemInformation newFolder = new FolderInformation()
+ {
+ Label = "",
+ IconPath = FolderInformation.FolderIconPath,
+ BadgeCount = 0,
+ IsRemovable = true,
+ };
+
+ newFolder.Id = AppsDataProvider.Instance.Set(newFolder);
+ return newFolder;
+ }
+
+ private async void SetAppsInformation()
+ {
+ var dbList = AppsDataProvider.Instance.Get();
+ var installedAppList = await PackageChangedNotifier.Instance.GetAllInstalledAppInformation();
+ var resultList = new ObservableCollection<ItemInformation>();
+
+ foreach (var appsInformation in dbList)
+ {
+ if (appsInformation is FolderInformation folderInfo)
+ {
+ folderInfo.IsRemovable = true;
+
+ var newAppList = new List<AppInformation>();
+ foreach (var folderApp in folderInfo.AppList)
+ {
+ AppInformation newInfo = new AppInformation(folderApp.GetDataObject());
+ if (UpdateAppInformation(installedAppList, newInfo))
+ {
+ newAppList.Add(newInfo);
+ }
+ }
+
+ folderInfo.ClearAllApps();
+ foreach (var newFolerApp in newAppList)
+ {
+ folderInfo.AddApp(newFolerApp);
+ }
+
+ resultList.Add(folderInfo);
+ }
+ else if (appsInformation is AppInformation appInfo)
+ {
+ if (UpdateAppInformation(installedAppList, appInfo))
+ {
+ resultList.Add(appInfo);
+ }
+ }
+ }
+
+ foreach (var newApp in installedAppList)
+ {
+ var badge = BadgeEventNotifier.Instance.Get(newApp.AppId);
+ var appInfo = new AppInformation()
+ {
+ Label = newApp.Title,
+ IconPath = newApp.IconPath,
+ BadgeCount = (badge != null) ? badge.Count : 0,
+ IsRemovable = newApp.IsRemovable,
+
+ PackageId = newApp.PackageId,
+ AppId = newApp.AppId,
+ IsInSdCard = newApp.IsInSdCard,
+ };
+ resultList.Add(appInfo);
+ }
+
+ resultList = new ObservableCollection<ItemInformation>(from i in resultList orderby i.Label select i);
+ AppsDataProvider.Instance.Set(resultList);
+ AppsInformation = resultList;
+ int index = 0;
+ foreach (var item in resultList)
+ {
+ item.Index = index++;
+ }
+ }
+
+ public void Print()
+ {
+ Log.Debug("------------------------------");
+ if (AppsInformation == null)
+ {
+ return;
+ }
+
+ foreach (var item in AppsInformation)
+ {
+ Log.Debug($"{item.Id}: {item.Index} : {item.Label}");
+ }
+
+ Log.Debug("------------------------------");
+ }
+
+ private bool UpdateAppInformation(
+ IList<InstalledAppInformation> installedAppList,
+ AppInformation appInfo)
+ {
+ foreach (var app in installedAppList)
+ {
+ if (app.PackageId.CompareTo(appInfo.PackageId) == 0)
+ {
+ appInfo.IsRemovable = app.IsRemovable;
+ appInfo.IsInSdCard = app.IsInSdCard;
+ installedAppList.Remove(app);
+
+ var badge = BadgeEventNotifier.Instance.Get(appInfo.AppId);
+ appInfo.BadgeCount = (badge != null) ? badge.Count : 0;
+
+ return true;
+ }
+ }
+
+ AppsDataProvider.Instance.Remove(appInfo);
+ return false;
+ }
+
+ private void MoveAppToFolder(ItemInformation info)
+ {
+ Log.Debug("called");
+ if (info is AppInformation appInfo)
+ {
+ foreach (var item in AppsInformation)
+ {
+ if (item.Id == appInfo.ParentId)
+ {
+ Log.Debug("" + (item as FolderInformation).Label);
+ (item as FolderInformation).AddApp(appInfo);
+ AppsInformation.Remove(appInfo);
+ AppsDataProvider.Instance.Update(appInfo);
+ Sort();
+ OpenedFolderInformation = new FolderInformation()
+ {
+ Id = FolderInformation.InitialId,
+ };
+ break;
+ }
+ }
+ }
+ }
+
+ private void MoveAppOutFolder(ItemInformation info)
+ {
+ Log.Debug("called");
+ if (info is AppInformation appInfo)
+ {
+ foreach (var item in AppsInformation)
+ {
+ if (item.Id == appInfo.ParentId)
+ {
+ (item as FolderInformation).RemoveApp(appInfo.Id);
+ (item as FolderInformation).HaveToUpdate = true;
+ OpenedFolderInformation = new FolderInformation()
+ {
+ Id = FolderInformation.InitialId,
+ };
+ appInfo.ParentId = ItemInformation.InitialId;
+ appInfo.Index = AppsInformation.Count;
+ AppsInformation.Add(appInfo);
+ AppsDataProvider.Instance.Update(appInfo);
+ Sort();
+ break;
+ }
+ }
+ }
+ }
+
+ private void Sort()
+ {
+ //Sort
+ ObservableCollection<ItemInformation> resultList =
+ new ObservableCollection<ItemInformation>(from i in AppsInformation orderby i.Label select i);
+
+ int index = 0;
+ foreach (var result in resultList)
+ {
+ int i = 0;
+ foreach (var item in AppsInformation)
+ {
+ if (item.Id == result.Id)
+ {
+ if (item.Index != index)
+ {
+ AppsInformation.Move(i, index);
+ }
+
+ break;
+ }
+
+ i++;
+ }
+
+ index++;
+ }
+
+ index = 0;
+ foreach (var item in AppsInformation)
+ {
+ (item as ItemInformation).Index = index++;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Xamarin.Forms;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// Class for DeviceInfo inherited from IDeviceInfo
+ /// </summary>
+ public class DeviceInfo : IDeviceInfo
+ {
+ /// <summary>
+ /// This Lazy() make DeviceInfo class can be constructed when there's a request for construction of DeviceInfo at first time
+ /// </summary>
+ private static readonly Lazy<DeviceInfo> lazy
+ = new Lazy<DeviceInfo>(() => new DeviceInfo());
+
+ /// <summary>
+ /// An accessors for DeviceInfo instance
+ /// </summary>
+ public static DeviceInfo Instance
+ {
+ get => lazy.Value;
+ }
+
+ private DeviceInfo()
+ {
+
+ }
+
+ /// <summary>
+ /// Gets device screen width
+ /// </summary>
+ public int Width
+ {
+ get
+ {
+ var device = DependencyService.Get<IDeviceInfo>();
+ return device == null ? 720 : device.Width;
+ }
+ }
+
+ /// <summary>
+ /// Gets device screen height
+ /// </summary>
+ public int Height
+ {
+ get
+ {
+ var device = DependencyService.Get<IDeviceInfo>();
+ return device == null ? 1280 : device.Height;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets device StatusBar mode
+ /// </summary>
+ public StatusBarMode StatusBarMode
+ {
+ get
+ {
+ var device = DependencyService.Get<IDeviceInfo>();
+ return device.StatusBarMode;
+ }
+
+ set
+ {
+ var device = DependencyService.Get<IDeviceInfo>();
+ device.StatusBarMode = value;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using System;
+using Xamarin.Forms;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// Represents messages which are used between View and ViewModel
+ /// </summary>
+ public enum Message
+ {
+ ShowApps,
+ ShowWidgets,
+ CreateFolder, /* To be to be deprecated */
+ }
+
+ /// <summary>
+ /// Class for MessagingCenter
+ /// </summary>
+ public class HomeMessagingCenter
+ {
+ /// <summary>
+ /// Register message will be sent
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="message">Message</param>
+ public static void Send(object sender, Message message)
+ {
+ Log.Debug($"Send Message : {message}");
+ MessagingCenter.Send<Object>(sender, message.ToString());
+ }
+
+ /// <summary>
+ /// Register message will be received
+ /// </summary>
+ /// <param name="sender">Sender</param>
+ /// <param name="message">Message</param>
+ /// <param name="callback">Callback.</param>
+ public static void Subscribe(object sender, Message message, Action<object> callback)
+ {
+ MessagingCenter.Subscribe<Object>(sender, message.ToString(), callback);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// A helper class for view models.
+ /// The ViewModelBase implements INotifyPropertyChanged and
+ /// provides OnPropertyChanged method to be used for notifying to views.
+ /// </summary>
+ public class ViewModelBase : INotifyPropertyChanged
+ {
+
+ /// <summary>
+ /// Occurs when property changed.
+ /// </summary>
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ /// <summary>
+ /// Raises the property changed event.
+ /// </summary>
+ /// <param name="propertyName">Property name.</param>
+ protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
+ {
+ var changed = PropertyChanged;
+ if (changed == null)
+ {
+ return;
+ }
+
+ changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.Debug;
+using Homescreen.Model;
+using System;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// Class for WallpaperPath inherited from ViewModelBase
+ /// </summary>
+ public class WallpaperPath : ViewModelBase
+ {
+ private string path;
+ private event EventHandler WallpaperChangedEventHandler;
+
+ /// <summary>
+ /// Property to access to path of image
+ /// </summary>
+ public string ImagePath
+ {
+ get => path;
+ private set
+ {
+ path = value;
+ OnPropertyChanged("ImagePath");
+ }
+ }
+
+ /// <summary>
+ /// Constructor for WallpaperPath
+ /// </summary>
+ public WallpaperPath()
+ {
+ SetPath();
+
+ WallpaperChangedEventHandler = new EventHandler((sender, arg) => SetPath());
+
+ WallpaperChangedNotifier.Instance.RegisterWallpaperChangedCallback(WallpaperChangedEventHandler);
+ }
+
+ /// <summary>
+ /// Set image path to ImagePath property
+ /// </summary>
+ private void SetPath()
+ {
+ var path = WallpaperChangedNotifier.Instance.GetWallpaperPath();
+ if (string.IsNullOrEmpty(path) || System.IO.File.Exists(path) == false)
+ {
+ path = "default_bg.png";
+ }
+
+ Log.Debug($"Set WallPaper : Path = {path}");
+
+ ImagePath = path;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 Homescreen.DataModel;
+using Homescreen.Debug;
+using Homescreen.Model;
+using Homescreen.ViewModel.Widgets;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Homescreen.ViewModel
+{
+ /// <summary>
+ /// Class for WidgetsInformationCenter which control Data read from Model
+ /// </summary>
+ public class WidgetsInformationCenter : ViewModelBase
+ {
+ /// <summary>
+ /// The maximum number of widgets that can be added to Widgets
+ /// </summary>
+ public const int MaxWidgetsPage = 6;
+ /// <summary>
+ /// Collection property have WidgetPageInformations
+ /// </summary>
+ private ObservableCollection<WidgetPageInformation> widgetsInformation;
+
+ /// <summary>
+ /// An accessors for widgetsInformation
+ /// </summary>
+ public ObservableCollection<WidgetPageInformation> WidgetsInformation
+ {
+ get => widgetsInformation;
+ private set
+ {
+ widgetsInformation = value;
+ OnPropertyChanged();
+ }
+ }
+
+ /// <summary>
+ /// Command binded with AddWidgetCommandProperty of AddWidgetPage
+ /// </summary>
+ public Command AddWidgetCommand => new Command<WidgetInformationToAdd>((widgetToAdd) => AddWidget(widgetToAdd));
+
+ /// <summary>
+ /// Command binded with DeleteWidgetCommandProperty of WidgetLayout
+ /// </summary>
+ public Command DeleteWidgetCommand => new Command<WidgetInformation>((widgetInfo) => DeleteWidget(widgetInfo));
+
+ /// <summary>
+ /// Command binded with UpdateWidgetCommandProperty of WidgetLayout and WidgetAllPageLayout
+ /// </summary>
+ public Command UpdateWidgetCommand
+ {
+ get => new Command<WidgetInformation>((information) =>
+ {
+ Log.Debug($"WidgetUpdate Command called : { information.PageIndex } : {information.WidgetId}");
+ foreach (var pageInfo in WidgetsInformation)
+ {
+ foreach (var widgetInfo in pageInfo.Widgets)
+ {
+ if (widgetInfo.Id == information.Id)
+ {
+ if (information.PageIndex == pageInfo.PageIndex)
+ {
+ for (int index = 0; index < pageInfo.Widgets.Count; index++)
+ {
+ if (pageInfo.Widgets[index].Id == information.Id)
+ {
+ pageInfo.Widgets[index] = information;
+ break;
+ }
+ }
+ }
+ else
+ {
+ WidgetsInformation[pageInfo.PageIndex]?.Widgets?.Remove(information);
+ WidgetsInformation[information.PageIndex]?.Widgets?.Add(information);
+ }
+
+ break;
+ }
+ }
+ }
+
+ HomeWidgetProvider.Instance.UpdateWidget(information);
+ });
+ }
+ /// <summary>
+ /// Command binded with AddWidgetPageCommandProperty of WidgetAllPageLayout
+ /// </summary>
+ public Command AddWidgetPageCommand => new Command(() => AddWidgetPage());
+ /// <summary>
+ /// Command binded with UpdateWidgetCommandProperty of WidgetAllPageLayout
+ /// </summary>
+ public Command DeleteWidgetPageCommand => new Command<WidgetPageInformation>((page) => DeleteWidgetPage(page));
+
+ private int pageCount;
+ public int PageCount
+ {
+ get => pageCount;
+ set
+ {
+ pageCount = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private double scrollPosition;
+ public double ScrollPosition
+ {
+ get => scrollPosition;
+ set
+ {
+ scrollPosition = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private Command scrollToPageCommand;
+ public Command ScrollToPageCommand
+ {
+ get => scrollToPageCommand;
+ set
+ {
+ scrollToPageCommand = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private Rectangle pickedWidgetPosition;
+ /// <summary>
+ /// The current location of the picked widget
+ /// </summary>
+ public Rectangle PickedWidgetPosition
+ {
+ get => pickedWidgetPosition;
+ set
+ {
+ pickedWidgetPosition = value;
+ OnPropertyChanged();
+ }
+ }
+
+ /// <summary>
+ /// Constructor for WidgetsInformationCenter
+ /// </summary>
+ public WidgetsInformationCenter()
+ {
+ Log.Debug("---Initialize WidgetsInformationCenter---");
+
+ Task.Run(() =>
+ {
+ InitWidgets();
+ });
+ }
+
+ /// <summary>
+ /// When WidgetsInformationCenter has been created first time, create widget list according to widget information read from Model
+ /// </summary>
+ private void InitWidgets()
+ {
+ Log.Debug("---Initialize Widgets by DB---");
+ int pageCount = HomeWidgetProvider.Instance.GetWidgetPageCount();
+
+ Log.Debug($"page count is {pageCount}.");
+ List<WidgetPageInformation> pageList = new List<WidgetPageInformation>();
+ for (int index = 0; index < pageCount; index++)
+ {
+ var list = HomeWidgetProvider.Instance.GetWidgetList(index);
+ Log.Debug($"Page {index} has {list.Count} widgets.");
+
+ pageList.Add(new WidgetPageInformation()
+ {
+ PageIndex = index,
+ Widgets = new ObservableCollection<WidgetInformation>(list)
+ });
+ }
+
+ WidgetsInformation = new ObservableCollection<WidgetPageInformation>(pageList);
+ }
+
+ /// <summary>
+ /// Adds WidgetPageInformation to WidgetInformation in this class and Model
+ /// </summary>
+ private void AddWidgetPage()
+ {
+ Log.Debug("WidgetInformationCenter : AddWidgetPage called");
+ int pageCount = HomeWidgetProvider.Instance.GetWidgetPageCount();
+
+ if (pageCount >= MaxWidgetsPage)
+ {
+ Log.Error($"Can not add a page anymore, The maximum number of pages is {MaxWidgetsPage}.");
+ return;
+ }
+
+ WidgetsInformation.Add(new WidgetPageInformation()
+ {
+ PageIndex = pageCount,
+ Widgets = new ObservableCollection<WidgetInformation>(),
+ });
+
+ HomeWidgetProvider.Instance.SetWidgetPageCount(WidgetsInformation.Count);
+ }
+
+ /// <summary>
+ /// Removes WidgetPage from WidgetInformation in this class and Model
+ /// </summary>
+ /// <param name="pageInfo">WidgetPageInformation will be removed</param>
+ private void DeleteWidgetPage(WidgetPageInformation pageInfo)
+ {
+ Log.Debug("DeleteWidgetPage called ");
+ if (widgetsInformation.Contains(pageInfo) == false)
+ {
+ Log.Error("The page information is invalid and can not be deleted.");
+ return;
+ }
+
+ if (widgetsInformation.Count <= 1)
+ {
+ Log.Error("There must be at least one widget page.");
+ return;
+ }
+
+ if (pageInfo.Widgets.Count > 0)
+ {
+ foreach (var widget in pageInfo.Widgets)
+ {
+ HomeWidgetProvider.Instance.DeleteWidget(widget);
+ }
+ }
+
+ WidgetsInformation.Remove(pageInfo);
+
+ foreach (var page in WidgetsInformation)
+ {
+ if (page.PageIndex > pageInfo.PageIndex)
+ {
+ page.PageIndex -= 1;
+ foreach (var widget in page.Widgets)
+ {
+ widget.PageIndex = page.PageIndex;
+ HomeWidgetProvider.Instance.UpdateWidget(widget);
+ }
+ }
+ }
+
+ HomeWidgetProvider.Instance.SetWidgetPageCount(WidgetsInformation.Count);
+ }
+
+ /// <summary>
+ /// Add Widget to the page
+ /// If there's no page have enough area for widget, add new page
+ /// </summary>
+ /// <param name="widgetInfo">WidgetInformation will be added</param>
+ private void AddWidget(WidgetInformationToAdd widgetInfo)
+ {
+ Log.Debug("Try to add a widget");
+ int pageIndex = widgetInfo.Info.PageIndex;
+ if (pageIndex >= 0)
+ {
+ if (TryAddWidgetAt(pageIndex, widgetInfo.Info))
+ {
+ Log.Debug($"Widget was added to page {pageIndex}");
+ }
+ else
+ {
+ Log.Debug($"Can not be added to Page {pageIndex}");
+ }
+
+ return;
+ }
+
+ int currentPage = (int)(ScrollPosition / (1.0 / PageCount) + 1E-5);
+ if (TryAddWidgetAt(currentPage, widgetInfo.Info))
+ {
+ Log.Debug($"Widget was added to current page {currentPage}");
+ return;
+ }
+
+ foreach (var page in WidgetsInformation)
+ {
+ if (TryAddWidgetAt(page.PageIndex, widgetInfo.Info))
+ {
+ Log.Debug($"Widget was added to another empty page {page.PageIndex}");
+
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { GoalPageIndex = page.PageIndex });
+ return;
+ }
+ }
+
+ AddWidgetPage();
+ if (TryAddWidgetAt(WidgetsInformation.Count - 1, widgetInfo.Info))
+ {
+ Log.Debug($"Widget was added to a new page {WidgetsInformation.Count - 1}");
+
+ Device.StartTimer(TimeSpan.FromMilliseconds(300), () =>
+ {
+ ScrollToPageCommand?.Execute(new ScrollToCommandArgs { Direction = ScrollDirection.Last });
+ return false;
+ });
+ return;
+ }
+
+ Log.Debug($"Widget was not added");
+ widgetInfo.ThrowAway();
+ }
+
+ /// <summary>
+ /// Delete WidgetInformation from WidgetsInformation of this class and Model
+ /// </summary>
+ /// <param name="widgetInfo">WidgetInformation will be removed</param>
+ private void DeleteWidget(WidgetInformation widgetInfo)
+ {
+ Log.Debug("WidgetDeleted Command called: " + widgetInfo.PageIndex + ":" + widgetInfo.WidgetId);
+
+ HomeWidgetProvider.Instance.DeleteWidget(widgetInfo);
+
+ WidgetsInformation[widgetInfo.PageIndex]?.Widgets?.Remove(widgetInfo);
+ }
+
+ /// <summary>
+ /// Try to add Widget to the page which positioned at pageIndex
+ /// </summary>
+ /// <param name="pageIndex">Index where Widget will be added</param>
+ /// <param name="widgetInfo">WidgetInformation will be added</param>
+ /// <returns>An add widget status. True if success.</returns>
+ private bool TryAddWidgetAt(int pageIndex, WidgetInformation widgetInfo)
+ {
+ Point emptyPosition = GetEmptyPosition(pageIndex, widgetInfo.Type);
+
+ if (emptyPosition.X < 0 || emptyPosition.Y < 0)
+ {
+ Log.Error($"You can not add a widget to Page {pageIndex} because there is no blank space on this page.");
+ return false;
+ }
+
+ widgetInfo.X = (int)emptyPosition.X;
+ widgetInfo.Y = (int)emptyPosition.Y;
+ widgetInfo.PageIndex = pageIndex;
+
+ HomeWidgetProvider.Instance.InsertWidget(widgetInfo);
+
+ WidgetsInformation[pageIndex]?.Widgets?.Add(widgetInfo);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Gets EmptyPosition where the widget can be added from the page which positioned at pageIndex
+ /// </summary>
+ /// <param name="pageIndex">Index of the page</param>
+ /// <param name="type">SizeType of the widget</param>
+ /// <returns>A position of empty space.</returns>
+ private Point GetEmptyPosition(int pageIndex, WidgetInformation.SizeType type)
+ {
+ IList<WidgetInformation> list = HomeWidgetProvider.Instance.GetWidgetList(pageIndex);
+
+ /*
+ * This algorithm assumes that only 4x4 and 4x2 widgets are supported.
+ */
+ if (list.Count >= 2)
+ {
+ return new Point(-1, -1);
+ }
+
+ if (list.Count == 0)
+ {
+ return new Point(0, 0);
+ }
+
+ /* list count is 1 */
+ if (type == WidgetInformation.SizeType.SIZE_4x4)
+ {
+ return new Point(-1, -1);
+ }
+
+ if (list[0].Type == WidgetInformation.SizeType.SIZE_4x4)
+ {
+ return new Point(-1, -1);
+ }
+
+ if (list[0].Y == 1)
+ {
+ return new Point(-1, -1);
+ }
+
+ return list[0].Y == 0 ? new Point(0, 2) : new Point(0, 0);
+ }
+ }
+
+ namespace Widgets
+ {
+ public enum ScrollDirection
+ {
+ First = 1024,
+ Previous = 2048,
+ Directly = 4096,
+ Next = 8192,
+ Last = 16384,
+ }
+
+ public class ScrollToCommandArgs
+ {
+ public ScrollDirection Direction { get; set; } = ScrollDirection.Directly;
+ public int GoalPageIndex { get; set; }
+ }
+ }
+}