Initial Setup 19/165519/1 tizen
authorChulSeung Kim <charles0.kim@samsung.com>
Tue, 2 Jan 2018 01:26:43 +0000 (10:26 +0900)
committerChulSeung Kim <charles0.kim@samsung.com>
Tue, 2 Jan 2018 01:30:06 +0000 (10:30 +0900)
Change-Id: Ia2b4276a0d73bf9a90aaa95d3aaad29c68daa576

251 files changed:
.gitignore [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/DebugLog/DebugLog.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Homescreen.UITest.Tizen.Mobile.csproj [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AlertPopup.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AppLauncher.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/BadgeNotifier.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/MenuPopup.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/PackageNotifier.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/RemoteViewStorage.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ReusableRemoteView.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/SQLitePort.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/TizenDeviceInfo.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ToastPopup.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WallpaperInfo.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WidgetManager.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Program.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/ClickableImageRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/EntryViewRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEvent.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEventRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/NinePatchRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Settings.StyleCop [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunit.framework.dll [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunitlite.dll [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_nor.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_press.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add_press.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_drag.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_icon_sdcard_hd.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_press_117.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_nor.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_press.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_nor.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_press.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_stroke.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_white_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg_stroke.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_icon.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_stroke.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_white_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_app_icon.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_impossible.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_possible.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_empty_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_popup_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_apps.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_bg.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_home.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_menu.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_background.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_container.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_center.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_current.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_unit.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/shared/res/Homescreen.UITest.Tizen.Mobile.png [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAddWidget.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAppsState.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestDB.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestFunction.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWallpaper.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsInformationCenter.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsProvider.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsState.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUIEdit.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUINormal.cs [new file with mode: 0644]
Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/tizen-manifest.xml [new file with mode: 0644]
Homescreen.sln [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/DebugLog/DebugLog.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.csproj [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/AlertPopup.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/AppLauncher.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/BadgeNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/MenuPopup.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/PackageNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/RemoteViewStorage.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/ReusableRemoteView.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/SQLitePort.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/TizenDeviceInfo.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/ToastPopup.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/WallpaperInfo.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Ports/WidgetManager.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/ClickableImageRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/EntryViewRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEvent.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEventRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/NinePatchRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/Settings.StyleCop [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/add_page_nor.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/add_page_press.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/all_page_add.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/all_page_add_press.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/all_page_drag.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/app_icon_sdcard_hd.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/app_press_117.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/btn_add_nor.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/btn_add_press.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_nor.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_press.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/color_check_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/color_check_stroke.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/color_check_white_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg_stroke.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/core_check_icon.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/core_check_stroke.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/core_check_white_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/default_app_icon.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/default_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_impossible.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_possible.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_empty_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/folder_popup_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/home_button_apps.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/home_button_bg.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/home_button_home.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/home_button_menu.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/icon.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_background.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_container.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_center.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_current.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_unit.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/shared/res/Homescreen.Tizen.Mobile.png [new file with mode: 0644]
Homescreen/Homescreen.Tizen.Mobile/tizen-manifest.xml [new file with mode: 0644]
Homescreen/Homescreen/DataModel/AppInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/BadgeInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/DataStorageObject.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/FolderInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/InstalledAppInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/ItemInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/ItemInformationDAO.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/WidgetInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DataModel/WidgetPageInformation.cs [new file with mode: 0644]
Homescreen/Homescreen/DebugLog/ExecutePrint.cs [new file with mode: 0644]
Homescreen/Homescreen/DebugLog/ILog.cs [new file with mode: 0644]
Homescreen/Homescreen/DebugLog/Log.cs [new file with mode: 0644]
Homescreen/Homescreen/Homescreen.cs [new file with mode: 0644]
Homescreen/Homescreen/Homescreen.csproj [new file with mode: 0644]
Homescreen/Homescreen/Model/AppsDataProvider.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/BadgeEventNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/BadgeUpdateEventArgs.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/HomeWidgetProvider.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/HomescreenDataStorage.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IAppLauncher.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IAppsDataProvider.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IBadgeEventNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IDeviceInfo.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IPackageChanged.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IRemoteViewStorage.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/ISQLite.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IWallpaperChanged.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/Interface/IWidgetManager.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/PackageChangedNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/PackageUpdateEventArgs.cs [new file with mode: 0644]
Homescreen/Homescreen/Model/WallpaperChangedNotifier.cs [new file with mode: 0644]
Homescreen/Homescreen/Settings.StyleCop [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/BadgeLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/BadgeLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/CheckBox.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/CheckBox.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ChooseState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/DragState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/EditState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/FolderLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/FolderLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ItemLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ItemLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Apps/NormalState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/ClickableImage.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/EntryView.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Event/ItemClickedEventArgs.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Event/ItemLayoutMouseEvent.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Event/WidgetLongPressEvent.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Event/WidgetPageEvent.cs [new file with mode: 0644]
Homescreen/Homescreen/View/HomeScrollView.cs [new file with mode: 0644]
Homescreen/Homescreen/View/HomescreenMainPage.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/HomescreenMainPage.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/ImageButton.cs [new file with mode: 0644]
Homescreen/Homescreen/View/IndicatorUnit.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/IndicatorUnit.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IAlertPopup.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IAppsLayout.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IAppsState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IAppsStateHandler.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IBackHomeSignalReciever.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IMenuPopup.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IMouseEventReceiver.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IToastPopup.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IVisibilityChanged.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IWidgetsLayout.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IWidgetsState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Interface/IWidgetsStateHandler.cs [new file with mode: 0644]
Homescreen/Homescreen/View/NinePatch.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/NinePatch.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/OptionButton.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/OptionButton.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/PageIndicator.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/PageIndicator.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Wallpaper.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Wallpaper.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AllpageState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/EditState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/FastScrollLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/NormalState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/PreviewImage.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/PreviewImage.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/ReorderState.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml [new file with mode: 0644]
Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/AppsInformationCenter.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/DeviceInfo.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/HomeMessagingCenter.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/ViewModelBase.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/WallpaperPath.cs [new file with mode: 0644]
Homescreen/Homescreen/ViewModel/WidgetsInformationCenter.cs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..281d592
--- /dev/null
@@ -0,0 +1,260 @@
+## 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
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/DebugLog/DebugLog.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/DebugLog/DebugLog.cs
new file mode 100644 (file)
index 0000000..fd760ab
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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);
+        }
+    }
+
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Homescreen.UITest.Tizen.Mobile.csproj b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Homescreen.UITest.Tizen.Mobile.csproj
new file mode 100644 (file)
index 0000000..70f86e6
--- /dev/null
@@ -0,0 +1,45 @@
+<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>
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AlertPopup.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AlertPopup.cs
new file mode 100644 (file)
index 0000000..97ba56b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AppLauncher.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/AppLauncher.cs
new file mode 100644 (file)
index 0000000..44cf5ee
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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;
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/BadgeNotifier.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/BadgeNotifier.cs
new file mode 100644 (file)
index 0000000..820544d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+        }
+    }
+
+
+
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/MenuPopup.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/MenuPopup.cs
new file mode 100644 (file)
index 0000000..096abf8
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/PackageNotifier.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/PackageNotifier.cs
new file mode 100644 (file)
index 0000000..d2aedfe
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/RemoteViewStorage.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/RemoteViewStorage.cs
new file mode 100644 (file)
index 0000000..42ddca1
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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}");
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ReusableRemoteView.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ReusableRemoteView.cs
new file mode 100644 (file)
index 0000000..818190c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/SQLitePort.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/SQLitePort.cs
new file mode 100644 (file)
index 0000000..7ebf360
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/TizenDeviceInfo.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/TizenDeviceInfo.cs
new file mode 100644 (file)
index 0000000..f1e4b47
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ToastPopup.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/ToastPopup.cs
new file mode 100644 (file)
index 0000000..61c004a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WallpaperInfo.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WallpaperInfo.cs
new file mode 100644 (file)
index 0000000..1827104
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WidgetManager.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Ports/WidgetManager.cs
new file mode 100644 (file)
index 0000000..a5c227c
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Program.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Program.cs
new file mode 100644 (file)
index 0000000..0a23d35
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/ClickableImageRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/ClickableImageRenderer.cs
new file mode 100644 (file)
index 0000000..1fd9a9a
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/EntryViewRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/EntryViewRenderer.cs
new file mode 100644 (file)
index 0000000..29952d4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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);
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs
new file mode 100644 (file)
index 0000000..34555ee
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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;
+                });
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs
new file mode 100644 (file)
index 0000000..e2ed88c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEvent.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEvent.cs
new file mode 100644 (file)
index 0000000..a0f6d79
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEventRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/MouseEventRenderer.cs
new file mode 100644 (file)
index 0000000..81dac82
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/NinePatchRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/NinePatchRenderer.cs
new file mode 100644 (file)
index 0000000..593c1b3
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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();
+
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs
new file mode 100644 (file)
index 0000000..94ad240
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Settings.StyleCop b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/Settings.StyleCop
new file mode 100644 (file)
index 0000000..837530c
--- /dev/null
@@ -0,0 +1,722 @@
+<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
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunit.framework.dll b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunit.framework.dll
new file mode 100644 (file)
index 0000000..d4f7ba6
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunit.framework.dll differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunitlite.dll b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunitlite.dll
new file mode 100644 (file)
index 0000000..8085eb0
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/lib/nunitlite.dll differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_nor.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_nor.png
new file mode 100644 (file)
index 0000000..0cd3d4c
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_nor.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_press.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_press.png
new file mode 100644 (file)
index 0000000..9c58f42
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/add_page_press.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add.png
new file mode 100644 (file)
index 0000000..b95b84d
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add_press.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add_press.png
new file mode 100644 (file)
index 0000000..512b74c
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_add_press.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_drag.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_drag.png
new file mode 100644 (file)
index 0000000..cd658dd
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/all_page_drag.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_icon_sdcard_hd.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_icon_sdcard_hd.png
new file mode 100644 (file)
index 0000000..6176d5f
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_icon_sdcard_hd.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_press_117.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_press_117.png
new file mode 100644 (file)
index 0000000..5acae9d
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/app_press_117.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_nor.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_nor.png
new file mode 100644 (file)
index 0000000..65bce6b
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_nor.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_press.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_press.png
new file mode 100644 (file)
index 0000000..e696fbf
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_add_press.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_nor.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_nor.png
new file mode 100644 (file)
index 0000000..f18f449
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_nor.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_press.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_press.png
new file mode 100644 (file)
index 0000000..2132f4e
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/btn_delete_press.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_bg.png
new file mode 100644 (file)
index 0000000..8ef19a4
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_stroke.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_stroke.png
new file mode 100644 (file)
index 0000000..cfdfb4b
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_stroke.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_white_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_white_bg.png
new file mode 100644 (file)
index 0000000..db31538
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/color_check_white_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg.png
new file mode 100644 (file)
index 0000000..4c90c3c
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg_stroke.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg_stroke.png
new file mode 100644 (file)
index 0000000..54f7e13
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_bg_stroke.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_icon.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_icon.png
new file mode 100644 (file)
index 0000000..1ca92f9
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_icon.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_stroke.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_stroke.png
new file mode 100644 (file)
index 0000000..04c3db9
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_stroke.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_white_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_white_bg.png
new file mode 100644 (file)
index 0000000..c246442
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/core_check_white_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_app_icon.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_app_icon.png
new file mode 100644 (file)
index 0000000..f6dc743
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_app_icon.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_bg.png
new file mode 100644 (file)
index 0000000..3d05599
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/default_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg.png
new file mode 100644 (file)
index 0000000..2100555
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_impossible.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_impossible.png
new file mode 100644 (file)
index 0000000..84d1fec
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_impossible.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_possible.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_possible.png
new file mode 100644 (file)
index 0000000..038a971
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_bg_possible.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_empty_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_empty_bg.png
new file mode 100644 (file)
index 0000000..b746faa
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_appicon_empty_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_popup_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_popup_bg.png
new file mode 100644 (file)
index 0000000..6a1c594
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/folder_popup_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_apps.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_apps.png
new file mode 100644 (file)
index 0000000..968b1b2
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_apps.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_bg.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_bg.png
new file mode 100644 (file)
index 0000000..a505579
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_bg.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_home.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_home.png
new file mode 100644 (file)
index 0000000..ec4cdcc
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_home.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_menu.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_menu.png
new file mode 100644 (file)
index 0000000..75eed42
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/home_button_menu.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_background.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_background.png
new file mode 100644 (file)
index 0000000..a5611de
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_background.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_container.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_container.png
new file mode 100644 (file)
index 0000000..ac4affc
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/icon_badge_container.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_center.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_center.png
new file mode 100644 (file)
index 0000000..3ad7d3d
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_center.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_current.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_current.png
new file mode 100644 (file)
index 0000000..d82d6ae
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_current.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_unit.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_unit.png
new file mode 100644 (file)
index 0000000..c36cc8b
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/res/page_indicator_unit.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/shared/res/Homescreen.UITest.Tizen.Mobile.png b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/shared/res/Homescreen.UITest.Tizen.Mobile.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/shared/res/Homescreen.UITest.Tizen.Mobile.png differ
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAddWidget.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAddWidget.cs
new file mode 100644 (file)
index 0000000..bffad75
--- /dev/null
@@ -0,0 +1,100 @@
+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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAppsState.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestAppsState.cs
new file mode 100644 (file)
index 0000000..370989a
--- /dev/null
@@ -0,0 +1,226 @@
+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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestDB.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestDB.cs
new file mode 100644 (file)
index 0000000..6b0c3d4
--- /dev/null
@@ -0,0 +1,146 @@
+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();
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestFunction.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestFunction.cs
new file mode 100644 (file)
index 0000000..12c49f3
--- /dev/null
@@ -0,0 +1,21 @@
+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));
+                }
+            }
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWallpaper.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWallpaper.cs
new file mode 100644 (file)
index 0000000..459e2ed
--- /dev/null
@@ -0,0 +1,74 @@
+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"));
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsInformationCenter.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsInformationCenter.cs
new file mode 100644 (file)
index 0000000..16a07e8
--- /dev/null
@@ -0,0 +1,336 @@
+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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsProvider.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsProvider.cs
new file mode 100644 (file)
index 0000000..57df317
--- /dev/null
@@ -0,0 +1,204 @@
+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));
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsState.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsState.cs
new file mode 100644 (file)
index 0000000..f409f30
--- /dev/null
@@ -0,0 +1,330 @@
+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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUIEdit.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUIEdit.cs
new file mode 100644 (file)
index 0000000..3a13c4e
--- /dev/null
@@ -0,0 +1,381 @@
+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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUINormal.cs b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/testcase/TestWidgetsUINormal.cs
new file mode 100644 (file)
index 0000000..b8766ed
--- /dev/null
@@ -0,0 +1,501 @@
+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);
+        }
+    }
+}
diff --git a/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/tizen-manifest.xml b/Homescreen.UITest/Homescreen.UITest.Tizen.Mobile/tizen-manifest.xml
new file mode 100644 (file)
index 0000000..0c2c375
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>
diff --git a/Homescreen.sln b/Homescreen.sln
new file mode 100644 (file)
index 0000000..58b60f7
--- /dev/null
@@ -0,0 +1,37 @@
+
+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
diff --git a/Homescreen/Homescreen.Tizen.Mobile/DebugLog/DebugLog.cs b/Homescreen/Homescreen.Tizen.Mobile/DebugLog/DebugLog.cs
new file mode 100644 (file)
index 0000000..fd760ab
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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);
+        }
+    }
+
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.cs b/Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.cs
new file mode 100644 (file)
index 0000000..63725d5
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.csproj b/Homescreen/Homescreen.Tizen.Mobile/Homescreen.Tizen.Mobile.csproj
new file mode 100644 (file)
index 0000000..133c412
--- /dev/null
@@ -0,0 +1,45 @@
+<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>
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/AlertPopup.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/AlertPopup.cs
new file mode 100644 (file)
index 0000000..97ba56b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/AppLauncher.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/AppLauncher.cs
new file mode 100644 (file)
index 0000000..44cf5ee
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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;
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/BadgeNotifier.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/BadgeNotifier.cs
new file mode 100644 (file)
index 0000000..820544d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+        }
+    }
+
+
+
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/MenuPopup.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/MenuPopup.cs
new file mode 100644 (file)
index 0000000..6e15d92
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/PackageNotifier.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/PackageNotifier.cs
new file mode 100644 (file)
index 0000000..d2aedfe
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/RemoteViewStorage.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/RemoteViewStorage.cs
new file mode 100644 (file)
index 0000000..9a11ade
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/ReusableRemoteView.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/ReusableRemoteView.cs
new file mode 100644 (file)
index 0000000..de535c8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/SQLitePort.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/SQLitePort.cs
new file mode 100644 (file)
index 0000000..33e7c5b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/TizenDeviceInfo.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/TizenDeviceInfo.cs
new file mode 100644 (file)
index 0000000..174de27
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/ToastPopup.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/ToastPopup.cs
new file mode 100644 (file)
index 0000000..61c004a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/WallpaperInfo.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/WallpaperInfo.cs
new file mode 100644 (file)
index 0000000..dc272e3
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Ports/WidgetManager.cs b/Homescreen/Homescreen.Tizen.Mobile/Ports/WidgetManager.cs
new file mode 100644 (file)
index 0000000..98ddd6b
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/ClickableImageRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/ClickableImageRenderer.cs
new file mode 100644 (file)
index 0000000..107a972
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/EntryViewRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/EntryViewRenderer.cs
new file mode 100644 (file)
index 0000000..29952d4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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);
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/FastScrollLayoutRenderer.cs
new file mode 100644 (file)
index 0000000..34555ee
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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;
+                });
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/HomeScrollViewRenderer.cs
new file mode 100644 (file)
index 0000000..e2ed88c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEvent.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEvent.cs
new file mode 100644 (file)
index 0000000..7e3b672
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEventRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/MouseEventRenderer.cs
new file mode 100644 (file)
index 0000000..81dac82
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/NinePatchRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/NinePatchRenderer.cs
new file mode 100644 (file)
index 0000000..c7456ca
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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();
+
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs b/Homescreen/Homescreen.Tizen.Mobile/Renderer/WidgetLayoutRenderer.cs
new file mode 100644 (file)
index 0000000..94ad240
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen.Tizen.Mobile/Settings.StyleCop b/Homescreen/Homescreen.Tizen.Mobile/Settings.StyleCop
new file mode 100644 (file)
index 0000000..837530c
--- /dev/null
@@ -0,0 +1,722 @@
+<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
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/add_page_nor.png b/Homescreen/Homescreen.Tizen.Mobile/res/add_page_nor.png
new file mode 100644 (file)
index 0000000..0cd3d4c
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/add_page_nor.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/add_page_press.png b/Homescreen/Homescreen.Tizen.Mobile/res/add_page_press.png
new file mode 100644 (file)
index 0000000..9c58f42
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/add_page_press.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add.png b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add.png
new file mode 100644 (file)
index 0000000..b95b84d
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add_press.png b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add_press.png
new file mode 100644 (file)
index 0000000..512b74c
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_add_press.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/all_page_drag.png b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_drag.png
new file mode 100644 (file)
index 0000000..cd658dd
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/all_page_drag.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/app_icon_sdcard_hd.png b/Homescreen/Homescreen.Tizen.Mobile/res/app_icon_sdcard_hd.png
new file mode 100644 (file)
index 0000000..6176d5f
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/app_icon_sdcard_hd.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/app_press_117.png b/Homescreen/Homescreen.Tizen.Mobile/res/app_press_117.png
new file mode 100644 (file)
index 0000000..5acae9d
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/app_press_117.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_nor.png b/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_nor.png
new file mode 100644 (file)
index 0000000..65bce6b
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_nor.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_press.png b/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_press.png
new file mode 100644 (file)
index 0000000..e696fbf
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/btn_add_press.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_nor.png b/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_nor.png
new file mode 100644 (file)
index 0000000..f18f449
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_nor.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_press.png b/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_press.png
new file mode 100644 (file)
index 0000000..2132f4e
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/btn_delete_press.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/color_check_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_bg.png
new file mode 100644 (file)
index 0000000..8ef19a4
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/color_check_stroke.png b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_stroke.png
new file mode 100644 (file)
index 0000000..cfdfb4b
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_stroke.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/color_check_white_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_white_bg.png
new file mode 100644 (file)
index 0000000..db31538
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/color_check_white_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg.png
new file mode 100644 (file)
index 0000000..4c90c3c
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg_stroke.png b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg_stroke.png
new file mode 100644 (file)
index 0000000..54f7e13
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_bg_stroke.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/core_check_icon.png b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_icon.png
new file mode 100644 (file)
index 0000000..1ca92f9
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_icon.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/core_check_stroke.png b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_stroke.png
new file mode 100644 (file)
index 0000000..04c3db9
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_stroke.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/core_check_white_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_white_bg.png
new file mode 100644 (file)
index 0000000..c246442
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/core_check_white_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/default_app_icon.png b/Homescreen/Homescreen.Tizen.Mobile/res/default_app_icon.png
new file mode 100644 (file)
index 0000000..f6dc743
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/default_app_icon.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/default_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/default_bg.png
new file mode 100644 (file)
index 0000000..3d05599
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/default_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg.png
new file mode 100644 (file)
index 0000000..2100555
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_impossible.png b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_impossible.png
new file mode 100644 (file)
index 0000000..84d1fec
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_impossible.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_possible.png b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_possible.png
new file mode 100644 (file)
index 0000000..038a971
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_bg_possible.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_empty_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_empty_bg.png
new file mode 100644 (file)
index 0000000..b746faa
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/folder_appicon_empty_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/folder_popup_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/folder_popup_bg.png
new file mode 100644 (file)
index 0000000..6a1c594
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/folder_popup_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/home_button_apps.png b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_apps.png
new file mode 100644 (file)
index 0000000..968b1b2
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_apps.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/home_button_bg.png b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_bg.png
new file mode 100644 (file)
index 0000000..a505579
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_bg.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/home_button_home.png b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_home.png
new file mode 100644 (file)
index 0000000..ec4cdcc
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_home.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/home_button_menu.png b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_menu.png
new file mode 100644 (file)
index 0000000..75eed42
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/home_button_menu.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/icon.png b/Homescreen/Homescreen.Tizen.Mobile/res/icon.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/icon.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_background.png b/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_background.png
new file mode 100644 (file)
index 0000000..a5611de
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_background.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_container.png b/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_container.png
new file mode 100644 (file)
index 0000000..ac4affc
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/icon_badge_container.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_center.png b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_center.png
new file mode 100644 (file)
index 0000000..3ad7d3d
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_center.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_current.png b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_current.png
new file mode 100644 (file)
index 0000000..d82d6ae
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_current.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_unit.png b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_unit.png
new file mode 100644 (file)
index 0000000..c36cc8b
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/res/page_indicator_unit.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/shared/res/Homescreen.Tizen.Mobile.png b/Homescreen/Homescreen.Tizen.Mobile/shared/res/Homescreen.Tizen.Mobile.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/Homescreen/Homescreen.Tizen.Mobile/shared/res/Homescreen.Tizen.Mobile.png differ
diff --git a/Homescreen/Homescreen.Tizen.Mobile/tizen-manifest.xml b/Homescreen/Homescreen.Tizen.Mobile/tizen-manifest.xml
new file mode 100644 (file)
index 0000000..2777673
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>
diff --git a/Homescreen/Homescreen/DataModel/AppInformation.cs b/Homescreen/Homescreen/DataModel/AppInformation.cs
new file mode 100644 (file)
index 0000000..d70bcdf
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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,
+            };
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/BadgeInformation.cs b/Homescreen/Homescreen/DataModel/BadgeInformation.cs
new file mode 100644 (file)
index 0000000..ea72b0a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/DataStorageObject.cs b/Homescreen/Homescreen/DataModel/DataStorageObject.cs
new file mode 100644 (file)
index 0000000..a44102c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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; }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/FolderInformation.cs b/Homescreen/Homescreen/DataModel/FolderInformation.cs
new file mode 100644 (file)
index 0000000..5c7fbae
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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,
+            };
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/InstalledAppInformation.cs b/Homescreen/Homescreen/DataModel/InstalledAppInformation.cs
new file mode 100644 (file)
index 0000000..bf3167a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/ItemInformation.cs b/Homescreen/Homescreen/DataModel/ItemInformation.cs
new file mode 100644 (file)
index 0000000..632c9e9
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/ItemInformationDAO.cs b/Homescreen/Homescreen/DataModel/ItemInformationDAO.cs
new file mode 100644 (file)
index 0000000..7243ca8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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; }
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/WidgetInformation.cs b/Homescreen/Homescreen/DataModel/WidgetInformation.cs
new file mode 100644 (file)
index 0000000..568874c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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;
+    }
+}
diff --git a/Homescreen/Homescreen/DataModel/WidgetPageInformation.cs b/Homescreen/Homescreen/DataModel/WidgetPageInformation.cs
new file mode 100644 (file)
index 0000000..405a8ed
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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; }
+    }
+}
diff --git a/Homescreen/Homescreen/DebugLog/ExecutePrint.cs b/Homescreen/Homescreen/DebugLog/ExecutePrint.cs
new file mode 100644 (file)
index 0000000..4406690
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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");
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/DebugLog/ILog.cs b/Homescreen/Homescreen/DebugLog/ILog.cs
new file mode 100644 (file)
index 0000000..70ff739
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/DebugLog/Log.cs b/Homescreen/Homescreen/DebugLog/Log.cs
new file mode 100644 (file)
index 0000000..3ff5dba
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Homescreen.cs b/Homescreen/Homescreen/Homescreen.cs
new file mode 100644 (file)
index 0000000..e6357f4
--- /dev/null
@@ -0,0 +1,41 @@
+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();
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Homescreen.csproj b/Homescreen/Homescreen/Homescreen.csproj
new file mode 100644 (file)
index 0000000..da41754
--- /dev/null
@@ -0,0 +1,90 @@
+<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>
diff --git a/Homescreen/Homescreen/Model/AppsDataProvider.cs b/Homescreen/Homescreen/Model/AppsDataProvider.cs
new file mode 100644 (file)
index 0000000..5265b1a
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * 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);
+                }
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/BadgeEventNotifier.cs b/Homescreen/Homescreen/Model/BadgeEventNotifier.cs
new file mode 100644 (file)
index 0000000..2babab0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/BadgeUpdateEventArgs.cs b/Homescreen/Homescreen/Model/BadgeUpdateEventArgs.cs
new file mode 100644 (file)
index 0000000..df1be33
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/HomeWidgetProvider.cs b/Homescreen/Homescreen/Model/HomeWidgetProvider.cs
new file mode 100644 (file)
index 0000000..2959d3e
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/HomescreenDataStorage.cs b/Homescreen/Homescreen/Model/HomescreenDataStorage.cs
new file mode 100644 (file)
index 0000000..e5626a3
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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>();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IAppLauncher.cs b/Homescreen/Homescreen/Model/Interface/IAppLauncher.cs
new file mode 100644 (file)
index 0000000..afb3ec0
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IAppsDataProvider.cs b/Homescreen/Homescreen/Model/Interface/IAppsDataProvider.cs
new file mode 100644 (file)
index 0000000..a1ef4ff
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IBadgeEventNotifier.cs b/Homescreen/Homescreen/Model/Interface/IBadgeEventNotifier.cs
new file mode 100644 (file)
index 0000000..4484e5c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IDeviceInfo.cs b/Homescreen/Homescreen/Model/Interface/IDeviceInfo.cs
new file mode 100644 (file)
index 0000000..8b75794
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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; }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IPackageChanged.cs b/Homescreen/Homescreen/Model/Interface/IPackageChanged.cs
new file mode 100644 (file)
index 0000000..20063d3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IRemoteViewStorage.cs b/Homescreen/Homescreen/Model/Interface/IRemoteViewStorage.cs
new file mode 100644 (file)
index 0000000..85109b6
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.Model
+{
+    public interface IRemoteViewStorage
+    {
+        void Delete(long widgetID);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/ISQLite.cs b/Homescreen/Homescreen/Model/Interface/ISQLite.cs
new file mode 100644 (file)
index 0000000..cdf4770
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IWallpaperChanged.cs b/Homescreen/Homescreen/Model/Interface/IWallpaperChanged.cs
new file mode 100644 (file)
index 0000000..1fbaf27
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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();
+    }
+}
diff --git a/Homescreen/Homescreen/Model/Interface/IWidgetManager.cs b/Homescreen/Homescreen/Model/Interface/IWidgetManager.cs
new file mode 100644 (file)
index 0000000..33a3cc5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/Model/PackageChangedNotifier.cs b/Homescreen/Homescreen/Model/PackageChangedNotifier.cs
new file mode 100644 (file)
index 0000000..6208284
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/PackageUpdateEventArgs.cs b/Homescreen/Homescreen/Model/PackageUpdateEventArgs.cs
new file mode 100644 (file)
index 0000000..4155a04
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Model/WallpaperChangedNotifier.cs b/Homescreen/Homescreen/Model/WallpaperChangedNotifier.cs
new file mode 100644 (file)
index 0000000..ccb5afc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/Settings.StyleCop b/Homescreen/Homescreen/Settings.StyleCop
new file mode 100644 (file)
index 0000000..837530c
--- /dev/null
@@ -0,0 +1,722 @@
+<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
diff --git a/Homescreen/Homescreen/View/Apps/AppsLayout.xaml b/Homescreen/Homescreen/View/Apps/AppsLayout.xaml
new file mode 100644 (file)
index 0000000..b7852e9
--- /dev/null
@@ -0,0 +1,60 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/AppsLayout.xaml.cs b/Homescreen/Homescreen/View/Apps/AppsLayout.xaml.cs
new file mode 100644 (file)
index 0000000..38d4339
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml b/Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml
new file mode 100644 (file)
index 0000000..447c4d1
--- /dev/null
@@ -0,0 +1,25 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml.cs b/Homescreen/Homescreen/View/Apps/AppsPageLayout.xaml.cs
new file mode 100644 (file)
index 0000000..76f810e
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml b/Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml
new file mode 100644 (file)
index 0000000..a913acc
--- /dev/null
@@ -0,0 +1,32 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml.cs b/Homescreen/Homescreen/View/Apps/AppsPageScrollView.xaml.cs
new file mode 100644 (file)
index 0000000..ddc0330
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/BadgeLayout.xaml b/Homescreen/Homescreen/View/Apps/BadgeLayout.xaml
new file mode 100644 (file)
index 0000000..88c7862
--- /dev/null
@@ -0,0 +1,27 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/BadgeLayout.xaml.cs b/Homescreen/Homescreen/View/Apps/BadgeLayout.xaml.cs
new file mode 100644 (file)
index 0000000..b64ddb1
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/CheckBox.xaml b/Homescreen/Homescreen/View/Apps/CheckBox.xaml
new file mode 100644 (file)
index 0000000..4428cdc
--- /dev/null
@@ -0,0 +1,23 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/CheckBox.xaml.cs b/Homescreen/Homescreen/View/Apps/CheckBox.xaml.cs
new file mode 100644 (file)
index 0000000..360a682
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/ChooseState.cs b/Homescreen/Homescreen/View/Apps/ChooseState.cs
new file mode 100644 (file)
index 0000000..a53668c
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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()
+        {
+
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml b/Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml
new file mode 100644 (file)
index 0000000..cdea7a4
--- /dev/null
@@ -0,0 +1,41 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml.cs b/Homescreen/Homescreen/View/Apps/ChooserTopBar.xaml.cs
new file mode 100644 (file)
index 0000000..691ac3e
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/DragState.cs b/Homescreen/Homescreen/View/Apps/DragState.cs
new file mode 100644 (file)
index 0000000..45b9243
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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()
+        {
+
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Apps/EditState.cs b/Homescreen/Homescreen/View/Apps/EditState.cs
new file mode 100644 (file)
index 0000000..0bf3a98
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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()
+        {
+
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Apps/FolderLayout.xaml b/Homescreen/Homescreen/View/Apps/FolderLayout.xaml
new file mode 100644 (file)
index 0000000..7b628c4
--- /dev/null
@@ -0,0 +1,57 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/FolderLayout.xaml.cs b/Homescreen/Homescreen/View/Apps/FolderLayout.xaml.cs
new file mode 100644 (file)
index 0000000..7ed4856
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/ItemLayout.xaml b/Homescreen/Homescreen/View/Apps/ItemLayout.xaml
new file mode 100644 (file)
index 0000000..818c0c9
--- /dev/null
@@ -0,0 +1,46 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/ItemLayout.xaml.cs b/Homescreen/Homescreen/View/Apps/ItemLayout.xaml.cs
new file mode 100644 (file)
index 0000000..4885bce
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml b/Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml
new file mode 100644 (file)
index 0000000..dab3efb
--- /dev/null
@@ -0,0 +1,27 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml.cs b/Homescreen/Homescreen/View/Apps/ItemThumbnail.xaml.cs
new file mode 100644 (file)
index 0000000..8e4f82a
--- /dev/null
@@ -0,0 +1,54 @@
+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
diff --git a/Homescreen/Homescreen/View/Apps/NormalState.cs b/Homescreen/Homescreen/View/Apps/NormalState.cs
new file mode 100644 (file)
index 0000000..08abc84
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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();
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/ClickableImage.xaml.cs b/Homescreen/Homescreen/View/ClickableImage.xaml.cs
new file mode 100644 (file)
index 0000000..a195b60
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/EntryView.cs b/Homescreen/Homescreen/View/EntryView.cs
new file mode 100644 (file)
index 0000000..6125b75
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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
+    {
+
+    }
+}
diff --git a/Homescreen/Homescreen/View/Event/ItemClickedEventArgs.cs b/Homescreen/Homescreen/View/Event/ItemClickedEventArgs.cs
new file mode 100644 (file)
index 0000000..945d32e
--- /dev/null
@@ -0,0 +1,14 @@
+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; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Event/ItemLayoutMouseEvent.cs b/Homescreen/Homescreen/View/Event/ItemLayoutMouseEvent.cs
new file mode 100644 (file)
index 0000000..645c635
--- /dev/null
@@ -0,0 +1,14 @@
+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; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Event/WidgetLongPressEvent.cs b/Homescreen/Homescreen/View/Event/WidgetLongPressEvent.cs
new file mode 100644 (file)
index 0000000..1f399d1
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Homescreen.View.Event
+{
+    public class WidgetLongPressEvent : EventArgs
+    {
+        public long LongPressedWidgetId { set; get; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Event/WidgetPageEvent.cs b/Homescreen/Homescreen/View/Event/WidgetPageEvent.cs
new file mode 100644 (file)
index 0000000..0352a24
--- /dev/null
@@ -0,0 +1,12 @@
+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; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/HomeScrollView.cs b/Homescreen/Homescreen/View/HomeScrollView.cs
new file mode 100644 (file)
index 0000000..896038c
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/HomescreenMainPage.xaml b/Homescreen/Homescreen/View/HomescreenMainPage.xaml
new file mode 100644 (file)
index 0000000..cda0adc
--- /dev/null
@@ -0,0 +1,35 @@
+<?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
diff --git a/Homescreen/Homescreen/View/HomescreenMainPage.xaml.cs b/Homescreen/Homescreen/View/HomescreenMainPage.xaml.cs
new file mode 100644 (file)
index 0000000..235a0c0
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/ImageButton.cs b/Homescreen/Homescreen/View/ImageButton.cs
new file mode 100644 (file)
index 0000000..ed9e27b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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);
+            };
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/IndicatorUnit.xaml b/Homescreen/Homescreen/View/IndicatorUnit.xaml
new file mode 100644 (file)
index 0000000..b17d050
--- /dev/null
@@ -0,0 +1,14 @@
+<?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
diff --git a/Homescreen/Homescreen/View/IndicatorUnit.xaml.cs b/Homescreen/Homescreen/View/IndicatorUnit.xaml.cs
new file mode 100644 (file)
index 0000000..11ee552
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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);
+            };
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IAlertPopup.cs b/Homescreen/Homescreen/View/Interface/IAlertPopup.cs
new file mode 100644 (file)
index 0000000..6711509
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IAppsLayout.cs b/Homescreen/Homescreen/View/Interface/IAppsLayout.cs
new file mode 100644 (file)
index 0000000..785597d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IAppsState.cs b/Homescreen/Homescreen/View/Interface/IAppsState.cs
new file mode 100644 (file)
index 0000000..8e42f46
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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();
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IAppsStateHandler.cs b/Homescreen/Homescreen/View/Interface/IAppsStateHandler.cs
new file mode 100644 (file)
index 0000000..5c40fce
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IBackHomeSignalReciever.cs b/Homescreen/Homescreen/View/Interface/IBackHomeSignalReciever.cs
new file mode 100644 (file)
index 0000000..e757aee
--- /dev/null
@@ -0,0 +1,23 @@
+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();
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IMenuPopup.cs b/Homescreen/Homescreen/View/Interface/IMenuPopup.cs
new file mode 100644 (file)
index 0000000..61b7394
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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();
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IMouseEventReceiver.cs b/Homescreen/Homescreen/View/Interface/IMouseEventReceiver.cs
new file mode 100644 (file)
index 0000000..93bdf3e
--- /dev/null
@@ -0,0 +1,31 @@
+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; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IToastPopup.cs b/Homescreen/Homescreen/View/Interface/IToastPopup.cs
new file mode 100644 (file)
index 0000000..ff11b0e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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);
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IVisibilityChanged.cs b/Homescreen/Homescreen/View/Interface/IVisibilityChanged.cs
new file mode 100644 (file)
index 0000000..5b6aefb
--- /dev/null
@@ -0,0 +1,9 @@
+namespace Homescreen.View.Interface
+{
+    public interface IVisibilityChanged : IBackHomeSignalReceiver
+    {
+        void Show(bool animation = true);
+
+        void Hide(bool animation = true);
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IWidgetsLayout.cs b/Homescreen/Homescreen/View/Interface/IWidgetsLayout.cs
new file mode 100644 (file)
index 0000000..0303d62
--- /dev/null
@@ -0,0 +1,10 @@
+namespace Homescreen.View.Widgets
+{
+    public interface IWidgetsLayout
+    {
+        OptionButton MenuButton { get; }
+        OptionButton AppsButton { get; }
+        WidgetPageScrollView PageScroller { get; }
+        AllpageLayout AllpageLayout { get; }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Interface/IWidgetsState.cs b/Homescreen/Homescreen/View/Interface/IWidgetsState.cs
new file mode 100644 (file)
index 0000000..1e4a7c5
--- /dev/null
@@ -0,0 +1,20 @@
+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
diff --git a/Homescreen/Homescreen/View/Interface/IWidgetsStateHandler.cs b/Homescreen/Homescreen/View/Interface/IWidgetsStateHandler.cs
new file mode 100644 (file)
index 0000000..8817399
--- /dev/null
@@ -0,0 +1,17 @@
+using System;
+
+namespace Homescreen.View.Widgets
+{
+    public enum WidgetsState
+    {
+        Normal,
+        Edit,
+        Reorder,
+        Allpage,
+    }
+
+    public interface IWidgetsStateHandler
+    {
+        void SetState(WidgetsState state, Enum args = null);
+    }
+}
diff --git a/Homescreen/Homescreen/View/NinePatch.xaml b/Homescreen/Homescreen/View/NinePatch.xaml
new file mode 100644 (file)
index 0000000..acc6df9
--- /dev/null
@@ -0,0 +1,5 @@
+<?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
diff --git a/Homescreen/Homescreen/View/NinePatch.xaml.cs b/Homescreen/Homescreen/View/NinePatch.xaml.cs
new file mode 100644 (file)
index 0000000..ebbdeaa
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/OptionButton.xaml b/Homescreen/Homescreen/View/OptionButton.xaml
new file mode 100644 (file)
index 0000000..517ce19
--- /dev/null
@@ -0,0 +1,20 @@
+<?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
diff --git a/Homescreen/Homescreen/View/OptionButton.xaml.cs b/Homescreen/Homescreen/View/OptionButton.xaml.cs
new file mode 100644 (file)
index 0000000..c92c5e2
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/PageIndicator.xaml b/Homescreen/Homescreen/View/PageIndicator.xaml
new file mode 100644 (file)
index 0000000..883ab9e
--- /dev/null
@@ -0,0 +1,7 @@
+<?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
diff --git a/Homescreen/Homescreen/View/PageIndicator.xaml.cs b/Homescreen/Homescreen/View/PageIndicator.xaml.cs
new file mode 100644 (file)
index 0000000..e984537
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Wallpaper.xaml b/Homescreen/Homescreen/View/Wallpaper.xaml
new file mode 100644 (file)
index 0000000..bf33ac7
--- /dev/null
@@ -0,0 +1,11 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Wallpaper.xaml.cs b/Homescreen/Homescreen/View/Wallpaper.xaml.cs
new file mode 100644 (file)
index 0000000..a24a5ea
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml b/Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml
new file mode 100644 (file)
index 0000000..30dd4f5
--- /dev/null
@@ -0,0 +1,23 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml.cs b/Homescreen/Homescreen/View/Widgets/AddWidgetPage.xaml.cs
new file mode 100644 (file)
index 0000000..70c8748
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml b/Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml
new file mode 100644 (file)
index 0000000..a9236bc
--- /dev/null
@@ -0,0 +1,24 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/AllpageLayout.xaml.cs
new file mode 100644 (file)
index 0000000..f1dc19b
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/AllpageState.cs b/Homescreen/Homescreen/View/Widgets/AllpageState.cs
new file mode 100644 (file)
index 0000000..13b917c
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml b/Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml
new file mode 100644 (file)
index 0000000..dc0596f
--- /dev/null
@@ -0,0 +1,52 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/AllpageThumbnailLayout.xaml.cs
new file mode 100644 (file)
index 0000000..d4ffa55
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/EditState.cs b/Homescreen/Homescreen/View/Widgets/EditState.cs
new file mode 100644 (file)
index 0000000..abab20f
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/FastScrollLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/FastScrollLayout.xaml.cs
new file mode 100644 (file)
index 0000000..ad7a190
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/NormalState.cs b/Homescreen/Homescreen/View/Widgets/NormalState.cs
new file mode 100644 (file)
index 0000000..2036fd5
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/View/Widgets/PreviewImage.xaml b/Homescreen/Homescreen/View/Widgets/PreviewImage.xaml
new file mode 100644 (file)
index 0000000..4d12784
--- /dev/null
@@ -0,0 +1,18 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/PreviewImage.xaml.cs b/Homescreen/Homescreen/View/Widgets/PreviewImage.xaml.cs
new file mode 100644 (file)
index 0000000..950924d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/ReorderState.cs b/Homescreen/Homescreen/View/Widgets/ReorderState.cs
new file mode 100644 (file)
index 0000000..21cb8af
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml b/Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml
new file mode 100644 (file)
index 0000000..4e9fada
--- /dev/null
@@ -0,0 +1,20 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/WidgetLayout.xaml.cs
new file mode 100644 (file)
index 0000000..60ba5bb
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml b/Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml
new file mode 100644 (file)
index 0000000..7658893
--- /dev/null
@@ -0,0 +1,38 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/WidgetPageLayout.xaml.cs
new file mode 100644 (file)
index 0000000..345bbdc
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml b/Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml
new file mode 100644 (file)
index 0000000..3e69937
--- /dev/null
@@ -0,0 +1,24 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml.cs b/Homescreen/Homescreen/View/Widgets/WidgetPageScrollView.xaml.cs
new file mode 100644 (file)
index 0000000..01efd9b
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml b/Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml
new file mode 100644 (file)
index 0000000..74d91e2
--- /dev/null
@@ -0,0 +1,38 @@
+<?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
diff --git a/Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml.cs b/Homescreen/Homescreen/View/Widgets/WidgetsLayout.xaml.cs
new file mode 100644 (file)
index 0000000..4049d76
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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
diff --git a/Homescreen/Homescreen/ViewModel/AppsInformationCenter.cs b/Homescreen/Homescreen/ViewModel/AppsInformationCenter.cs
new file mode 100644 (file)
index 0000000..2e82840
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * 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++;
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/ViewModel/DeviceInfo.cs b/Homescreen/Homescreen/ViewModel/DeviceInfo.cs
new file mode 100644 (file)
index 0000000..2b9656c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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;
+            }
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/ViewModel/HomeMessagingCenter.cs b/Homescreen/Homescreen/ViewModel/HomeMessagingCenter.cs
new file mode 100644 (file)
index 0000000..0b9a49d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/ViewModel/ViewModelBase.cs b/Homescreen/Homescreen/ViewModel/ViewModelBase.cs
new file mode 100644 (file)
index 0000000..c0259b1
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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));
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/ViewModel/WallpaperPath.cs b/Homescreen/Homescreen/ViewModel/WallpaperPath.cs
new file mode 100644 (file)
index 0000000..a5035e4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/Homescreen/Homescreen/ViewModel/WidgetsInformationCenter.cs b/Homescreen/Homescreen/ViewModel/WidgetsInformationCenter.cs
new file mode 100644 (file)
index 0000000..68235db
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * 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; }
+        }
+    }
+}