2 * Copyright (c) 2017 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using TVApps.ViewModels;
20 using System.ComponentModel;
21 using LibTVRefCommonPortable.Utils;
22 using System.Threading;
23 using System.Threading.Tasks;
24 using System.Windows.Input;
25 using System.Collections.Generic;
27 using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
28 using TVApps.Controls;
29 using Tizen.Xamarin.Forms.Extension;
31 namespace TVApps.Views
33 using Tizen = Xamarin.Forms.PlatformConfiguration.Tizen;
35 /// A custom view for displaying main page of TV Apps
37 public partial class MainPage : ContentPage
39 private DropdownList sortList;
40 private Button doneButton;
41 private Button cancelButton;
42 private static int pinnedAppCount = 0;
45 /// SubPanel icon's transition height value when it focused.
47 private int showTransitionHeight = SizeUtils.GetHeightSize(12);
50 /// Identifies the CurrentStatus bindable property
52 public static readonly BindableProperty CurrentStatusProperty = BindableProperty.Create("CurrentStatus", typeof(AppsStatus), typeof(MainPage), default(AppsStatus));
55 /// Gets or sets current status of MainPage
57 public AppsStatus CurrentStatus
59 get { return (AppsStatus)GetValue(CurrentStatusProperty); }
60 set { SetValue(CurrentStatusProperty, value); }
64 /// Identifies the app label bindable property
66 public static readonly BindableProperty DeletePopupAppLabelProperty = BindableProperty.Create("DeletePopupAppLabel", typeof(string), typeof(MainPage), default(string));
69 /// Gets or sets the app label to display at the delete popup
71 public string DeletePopupAppLabel
73 get { return (string)GetValue(DeletePopupAppLabelProperty); }
74 set { SetValue(DeletePopupAppLabelProperty, value); }
78 /// Identifies the delete popup visibility bindable property
80 public static readonly BindableProperty IsEnabledDeletePopupProperty = BindableProperty.Create("IsEnabledDeletePopup", typeof(bool), typeof(MainPage), default(bool));
83 /// Gets or sets whether delete popup is displayed
85 public bool IsEnabledDeletePopup
87 get { return (bool)GetValue(IsEnabledDeletePopupProperty); }
88 set { SetValue(IsEnabledDeletePopupProperty, value); }
92 /// Identifies the pin app control request received information bindable property
94 public static readonly BindableProperty IsPinAppRequestedProperty = BindableProperty.Create("IsPinAppRequested", typeof(bool), typeof(MainPage), default(bool));
97 /// Gets or sets whether pin app control request received information.
99 public bool IsPinAppRequested
101 get { return (bool)GetValue(IsPinAppRequestedProperty); }
102 set { SetValue(IsPinAppRequestedProperty, value); }
106 /// Identifies the delete popup command bindable property
108 public static readonly BindableProperty DeletePopupCommandProperty = BindableProperty.Create("DeletePopupCommand", typeof(Command), typeof(MainPage), default(Command));
111 /// A command will be executed if the button of the delete popup is clicked
113 public ICommand DeletePopupCommand
115 get { return (ICommand)GetValue(DeletePopupCommandProperty); }
116 set { SetValue(DeletePopupCommandProperty, value); }
120 /// Identifies the SumOfCheckedApp bindable property
122 public static readonly BindableProperty SumOfCheckedAppProperty = BindableProperty.Create("SumOfCheckedApp", typeof(int), typeof(MainPage), default(int));
125 /// Gets or sets count of checked AppItemCell
127 public int SumOfCheckedApp
129 get { return (int)GetValue(SumOfCheckedAppProperty); }
130 set { SetValue(SumOfCheckedAppProperty, value); }
134 /// Identifies the PinnedAppName bindable property
136 public static readonly BindableProperty PinnedAppNameProperty = BindableProperty.Create("PinnedAppName", typeof(string), typeof(MainPage), "");
139 /// Gets or sets name of pinned app
141 public string PinnedAppName
143 get { return (string)GetValue(PinnedAppNameProperty); }
144 set { SetValue(PinnedAppNameProperty, value); }
148 /// Identifies the PinnedAppName bindable property
150 public static readonly BindableProperty UnpinnedAppNameProperty = BindableProperty.Create("UnpinnedAppName", typeof(string), typeof(MainPage), "");
153 /// Gets or sets name of unpinned app
155 public string UnpinnedAppName
157 get { return (string)GetValue(UnpinnedAppNameProperty); }
158 set { SetValue(UnpinnedAppNameProperty, value); }
161 private void PlayHideAnimation()
163 var animation = new Animation();
164 var headerAnimation = new Animation(v => Header.Opacity = v, 1, 0);
165 var titleAnimation = new Animation(v => TitleLabel.Opacity = v, 1, 0);
166 var footerAnimation = new Animation(v => FooterNormal.Opacity = v, 1, 0);
167 var appListHeightAnimation = new Animation(v => AppList.TranslationY = v, 0, SizeUtils.GetHeightSize(12));
168 var appListAnimation = new Animation(v => AppList.Opacity = v, 1, 0);
169 var mainAnimation = new Animation(v => this.Opacity = v, 1, 0);
171 animation.Add(0.2, 0.8, headerAnimation);
172 animation.Add(0.2, 0.8, titleAnimation);
173 animation.Add(0.2, 0.8, footerAnimation);
174 animation.Add(0.2, 1, appListHeightAnimation);
175 animation.Add(0.2, 0.8, appListAnimation);
176 animation.Add(0.2, 1, mainAnimation);
178 animation.Commit(this, "QuitAnimation", 16, 834);
184 /// Adds PropertyChanged event handler and MenuKey event listener
188 InitializeComponent();
190 int backKeyImageSize = SizeUtils.GetHeightSize(40);
191 BackKeyInfoImage.WidthRequest = backKeyImageSize;
192 BackKeyInfoImage.HeightRequest = backKeyImageSize;
193 BackKeyInfo.FontSize = SizeUtils.GetFontSize(28);
194 BackKeyInfo.Margin = new Thickness(SizeUtils.GetWidthSize(6), 0, 0, 0);
195 AdditionalInfoText1.FontSize = SizeUtils.GetFontSize(32);
196 AdditionalInfoText2.FontSize = SizeUtils.GetFontSize(32);
198 TitleLabel.On<Tizen>().SetFontWeight(FontWeight.Normal);
199 BackKeyInfo.On<Tizen>().SetFontWeight(FontWeight.Normal);
200 AdditionalInfoIs.On<Tizen>().SetFontWeight(FontWeight.Light);
202 PropertyChanged += MainPagePropertyChanged;
203 SetCurrentStatus(AppsStatus.Default);
205 // TODO: This code is temporary for menu option test
206 App.SetMenuKeyListener((e, arg) =>
208 DebuggingUtils.Dbg("[Apps] Menu key is pressed");
210 if (CurrentStatus == AppsStatus.Default)
212 MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.LongPress.ToString());
216 AppList.OnChangeFocusChainingCommand = new Command(() =>
219 AppList.InitializeFocus();
223 protected override async void OnAppearing()
227 await AppList.TranslateTo(0, showTransitionHeight, 0);
228 #pragma warning disable CS4014
229 AppList.TranslateTo(0, 0, 667);
230 #pragma warning restore CS4014
231 await PageDimBox.FadeTo(0.0, 667);
232 PageDimBox.IsVisible = false;
234 AppList.InitializeFocus();
237 pinnedAppCount = SumOfCheckedApp;
240 private void MakeFocusChaining()
242 List<View> upperList = AppList.GetAppsUpperList().ToList();
243 List<View> lowerList = AppList.GetAppsLowerList().ToList();
245 int upperCount = upperList.Count;
246 int lowerCount = lowerList.Count;
252 for (int i = 1; i < upperCount-1; i++)
254 prevButton = upperList[i-1].FindByName<Button>("ButtonFocusArea");
255 button = upperList[i].FindByName<Button>("ButtonFocusArea");
256 nextButton = upperList[i + 1].FindByName<Button>("ButtonFocusArea");
258 SetFocusChainingLeftAndRight(prevButton, button);
259 SetFocusChainingLeftAndRight(button, nextButton);
263 prevButton?.On<Tizen>()?.SetNextFocusDownView(
264 lowerList[i - 1].FindByName<Button>("ButtonFocusArea"));
267 button?.On<Tizen>()?.SetNextFocusDownView(
268 lowerList[i].FindByName<Button>("ButtonFocusArea"));
270 if (i == upperCount-2)
272 button?.On<Tizen>()?.SetNextFocusDownView(lowerList[i].FindByName<Button>("ButtonFocusArea"));
274 nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
275 nextButton?.On<Tizen>()?.SetNextFocusDownView(
276 lowerList[lowerCount - 1].FindByName<Button>("ButtonFocusArea"));
280 for (int i = 1; i < lowerCount - 1; i++)
282 prevButton = lowerList[i - 1].FindByName<Button>("ButtonFocusArea");
283 button = lowerList[i].FindByName<Button>("ButtonFocusArea");
284 nextButton = lowerList[i + 1].FindByName<Button>("ButtonFocusArea");
286 SetFocusChainingLeftAndRight(prevButton, button);
287 SetFocusChainingLeftAndRight(button, nextButton);
291 prevButton?.On<Tizen>()?.SetNextFocusUpView(
292 upperList[i - 1].FindByName<Button>("ButtonFocusArea"));
295 button?.On<Tizen>()?.SetNextFocusUpView(
296 upperList[i].FindByName<Button>("ButtonFocusArea"));
298 if (i == lowerCount-2)
300 nextButton?.On<Tizen>()?.SetNextFocusUpView(upperList[i+1].FindByName<Button>("ButtonFocusArea"));
302 if (upperCount > lowerCount)
304 nextButton?.On<Tizen>()?.SetNextFocusRightView(upperList[upperCount-1].FindByName<Button>("ButtonFocusArea"));
308 nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
313 SetFocusChainingWithCurrentStatus(CurrentStatus);
316 private void SetFocusChainingLeftAndRight(Button left, Button right)
318 left?.On<Tizen>()?.SetNextFocusRightView(right);
319 right?.On<Tizen>()?.SetNextFocusLeftView(left);
322 private void SetFocusChainingUpAndDownForDefaultMode()
324 List<View> lowerList = AppList.GetAppsLowerList().ToList();
325 sortList = FooterNormal.GetSortDropdownList();
327 foreach (var item in lowerList)
329 Button button = item.FindByName<Button>("ButtonFocusArea");
330 button?.On<Tizen>()?.SetNextFocusDownView(sortList);
334 private void SetFocusChainingUpAndDownForDeleteMode()
336 List<View> lowerList = AppList.GetAppsLowerList().ToList();
337 cancelButton = FooterDelete.GetCancelButton();
339 foreach (var item in lowerList)
341 Button button = item.FindByName<Button>("ButtonFocusArea");
342 button?.On<Tizen>()?.SetNextFocusDownView(cancelButton);
345 if (lowerList.Count > 0)
347 cancelButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
350 cancelButton?.On<Tizen>()?.SetNextFocusLeftView(cancelButton);
353 private void SetFocusChainingUpAndDownForPinMode()
355 List<View> lowerList = AppList.GetAppsLowerList().ToList();
356 doneButton = FooterPin.GetDoneButton();
358 foreach (var item in lowerList)
360 Button button = item.FindByName<Button>("ButtonFocusArea");
361 button?.On<Tizen>()?.SetNextFocusDownView(doneButton);
364 if (lowerList.Count > 0 )
366 doneButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
369 doneButton?.On<Tizen>()?.SetNextFocusLeftView(doneButton);
372 private void SetFocusChainingWithCurrentStatus(AppsStatus status)
376 case AppsStatus.Default:
377 SetFocusChainingUpAndDownForDefaultMode();
380 SetFocusChainingUpAndDownForPinMode();
382 case AppsStatus.Delete:
383 SetFocusChainingUpAndDownForDeleteMode();
385 case AppsStatus.LongPress:
386 SetFocusChainingUpAndDownForDefaultMode();
392 /// A method sets current status of MainPage
393 /// Changes visibility of footers
395 /// <param name="status">The next status name</param>
396 private void SetCurrentStatus(AppsStatus status)
400 case AppsStatus.Default:
401 FooterNormal.IsVisible = true;
402 FooterPin.IsVisible = false;
403 FooterDelete.IsVisible = false;
404 BackKeyInfo.Text = "Quit";
405 AddtionalInfo.IsVisible = false;
406 TitleLabel.IsVisible = true;
407 AppList.InitializeFocus();
410 FooterNormal.IsVisible = false;
411 FooterPin.IsVisible = true;
412 FooterDelete.IsVisible = false;
413 BackKeyInfo.Text = "Front";
415 case AppsStatus.Delete:
416 FooterNormal.IsVisible = false;
417 FooterPin.IsVisible = false;
418 FooterDelete.IsVisible = true;
419 BackKeyInfo.Text = "Front";
421 case AppsStatus.LongPress:
422 FooterNormal.IsVisible = true;
423 FooterPin.IsVisible = false;
424 FooterDelete.IsVisible = false;
425 BackKeyInfo.Text = "Front";
433 /// This method is called when the properties of MainPage is changed
434 /// If CurrentStatus is changed, call SetCurrentStatus method
436 /// <param name="sender">The source of the event</param>
437 /// <param name="e">The event that is occurred when property of MainPage is changed</param>
438 private async void MainPagePropertyChanged(object sender, PropertyChangedEventArgs e)
440 if (e.PropertyName.Equals("CurrentStatus"))
442 SetCurrentStatus(CurrentStatus);
444 else if (e.PropertyName.Equals("IsEnabledDeletePopup"))
446 if (IsEnabledDeletePopup)
448 bool answer = await DisplayAlert(DeletePopupAppLabel, "Do you want to delete?", "YES", "NO");
449 Dictionary<string, string> ret = new Dictionary<string, string>();
450 ret.Add("AppLabel", DeletePopupAppLabel);
451 ret.Add("answer", answer ? "yes" : "no");
452 DeletePopupCommand?.Execute(ret);
456 AppList.InitializeFocus();
459 else if (e.PropertyName.Equals("SumOfCheckedApp"))
461 if (CurrentStatus == AppsStatus.Pin)
463 AddtionalInfo.IsVisible = true;
464 TitleLabel.IsVisible = false;
465 AdditionalInfoText1.On<Tizen>().SetFontWeight(FontWeight.Medium);
466 AdditionalInfoText2.On<Tizen>().SetFontWeight(FontWeight.Light);
467 AdditionalInfoIs.On<Tizen>().SetFontWeight(FontWeight.Light);
468 if (pinnedAppCount > SumOfCheckedApp)
470 AdditionalInfoText1.Text = UnpinnedAppName;
471 AdditionalInfoText2.Text = "Unpinned";
475 AdditionalInfoText1.Text = PinnedAppName;
476 AdditionalInfoText2.Text = "Pinned";
479 pinnedAppCount = SumOfCheckedApp;
485 /// A task for handling BackKey event
487 /// <returns>Always returns true</returns>
488 private async Task<bool> OnBackKeyPressedAtMain()
490 if (CurrentStatus != AppsStatus.Default)
492 MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.Default.ToString());
496 if (!AppList.IsFirstItemFocused)
498 AppList.InitializeFocus();
502 var answer = await DisplayAlert("QUIT", "Do you want to quit?", "YES", "NO");
506 await Task.Delay(800);
507 AppControlUtils.SelfTerminate();
511 AppList.InitializeFocus();
518 /// This method is called when Back button is pressed
520 /// <returns>Always returns true</returns>
521 /// <see cref="Page.OnBackButtonPressed"/>
522 protected override bool OnBackButtonPressed()
524 if (IsPinAppRequested)
529 SynchronizationContext.Current.Post(async (o) =>
531 await OnBackKeyPressedAtMain();