Adjust font weight & font family
[profile/tv/apps/dotnet/home.git] / TVApps / TVApps / Views / MainPage.xaml.cs
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd
3  *
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
7  *
8  *     http://floralicense.org/license/
9  *
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.
15  */
16
17 using Xamarin.Forms;
18
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;
26 using System.Linq;
27 using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
28 using TVApps.Controls;
29 using Tizen.Xamarin.Forms.Extension;
30
31 namespace TVApps.Views
32 {
33     using Tizen = Xamarin.Forms.PlatformConfiguration.Tizen;
34     /// <summary>
35     /// A custom view for displaying main page of TV Apps
36     /// </summary>
37     public partial class MainPage : ContentPage
38     {
39         private DropdownList sortList;
40         private Button doneButton;
41         private Button cancelButton;
42         private static int pinnedAppCount = 0;
43
44         /// <summary>
45         /// SubPanel icon's transition height value when it focused.
46         /// </summary>
47         private int showTransitionHeight = SizeUtils.GetHeightSize(12);
48
49         /// <summary>
50         /// Identifies the CurrentStatus bindable property
51         /// </summary>
52         public static readonly BindableProperty CurrentStatusProperty = BindableProperty.Create("CurrentStatus", typeof(AppsStatus), typeof(MainPage), default(AppsStatus));
53
54         /// <summary>
55         /// Gets or sets current status of MainPage
56         /// </summary>
57         public AppsStatus CurrentStatus
58         {
59             get { return (AppsStatus)GetValue(CurrentStatusProperty); }
60             set { SetValue(CurrentStatusProperty, value); }
61         }
62
63         /// <summary>
64         /// Identifies the app label bindable property
65         /// </summary>
66         public static readonly BindableProperty DeletePopupAppLabelProperty = BindableProperty.Create("DeletePopupAppLabel", typeof(string), typeof(MainPage), default(string));
67
68         /// <summary>
69         /// Gets or sets the app label to display at the delete popup
70         /// </summary>
71         public string DeletePopupAppLabel
72         {
73             get { return (string)GetValue(DeletePopupAppLabelProperty); }
74             set { SetValue(DeletePopupAppLabelProperty, value); }
75         }
76
77         /// <summary>
78         /// Identifies the delete popup visibility bindable property
79         /// </summary>
80         public static readonly BindableProperty IsEnabledDeletePopupProperty = BindableProperty.Create("IsEnabledDeletePopup", typeof(bool), typeof(MainPage), default(bool));
81
82         /// <summary>
83         /// Gets or sets whether delete popup is displayed
84         /// </summary>
85         public bool IsEnabledDeletePopup
86         {
87             get { return (bool)GetValue(IsEnabledDeletePopupProperty); }
88             set { SetValue(IsEnabledDeletePopupProperty, value); }
89         }
90
91         /// <summary>
92         /// Identifies the pin app control request received information bindable property
93         /// </summary>
94         public static readonly BindableProperty IsPinAppRequestedProperty = BindableProperty.Create("IsPinAppRequested", typeof(bool), typeof(MainPage), default(bool));
95
96         /// <summary>
97         /// Gets or sets whether pin app control request received information.
98         /// </summary>
99         public bool IsPinAppRequested
100         {
101             get { return (bool)GetValue(IsPinAppRequestedProperty); }
102             set { SetValue(IsPinAppRequestedProperty, value); }
103         }
104
105         /// <summary>
106         /// Identifies the delete popup command bindable property
107         /// </summary>
108         public static readonly BindableProperty DeletePopupCommandProperty = BindableProperty.Create("DeletePopupCommand", typeof(Command), typeof(MainPage), default(Command));
109
110         /// <summary>
111         /// A command will be executed if the button of the delete popup is clicked
112         /// </summary>
113         public ICommand DeletePopupCommand
114         {
115             get { return (ICommand)GetValue(DeletePopupCommandProperty); }
116             set { SetValue(DeletePopupCommandProperty, value); }
117         }
118
119         /// <summary>
120         /// Identifies the SumOfCheckedApp bindable property
121         /// </summary>
122         public static readonly BindableProperty SumOfCheckedAppProperty = BindableProperty.Create("SumOfCheckedApp", typeof(int), typeof(MainPage), default(int));
123
124         /// <summary>
125         /// Gets or sets count of checked AppItemCell
126         /// </summary>
127         public int SumOfCheckedApp
128         {
129             get { return (int)GetValue(SumOfCheckedAppProperty); }
130             set { SetValue(SumOfCheckedAppProperty, value); }
131         }
132
133         /// <summary>
134         /// Identifies the PinnedAppName bindable property
135         /// </summary>
136         public static readonly BindableProperty PinnedAppNameProperty = BindableProperty.Create("PinnedAppName", typeof(string), typeof(MainPage), "");
137
138         /// <summary>
139         /// Gets or sets name of pinned app
140         /// </summary>
141         public string PinnedAppName
142         {
143             get { return (string)GetValue(PinnedAppNameProperty); }
144             set { SetValue(PinnedAppNameProperty, value); }
145         }
146
147         /// <summary>
148         /// Identifies the PinnedAppName bindable property
149         /// </summary>
150         public static readonly BindableProperty UnpinnedAppNameProperty = BindableProperty.Create("UnpinnedAppName", typeof(string), typeof(MainPage), "");
151
152         /// <summary>
153         /// Gets or sets name of unpinned app
154         /// </summary>
155         public string UnpinnedAppName
156         {
157             get { return (string)GetValue(UnpinnedAppNameProperty); }
158             set { SetValue(UnpinnedAppNameProperty, value); }
159         }
160
161         private void PlayHideAnimation()
162         {
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);
170
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);
177
178             animation.Commit(this, "QuitAnimation", 16, 834);
179         }
180
181
182         /// <summary>
183         /// A constructor
184         /// Adds PropertyChanged event handler and MenuKey event listener
185         /// </summary>
186         public MainPage()
187         {
188             InitializeComponent();
189
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);
197
198             TitleLabel.On<Tizen>().SetFontWeight(FontWeight.Normal);
199             BackKeyInfo.On<Tizen>().SetFontWeight(FontWeight.Normal);
200             AdditionalInfoIs.On<Tizen>().SetFontWeight(FontWeight.Light);
201
202             PropertyChanged += MainPagePropertyChanged;
203             SetCurrentStatus(AppsStatus.Default);
204
205             // TODO: This code is temporary for menu option test
206             App.SetMenuKeyListener((e, arg) =>
207             {
208                 DebuggingUtils.Dbg("[Apps] Menu key is pressed");
209
210                 if (CurrentStatus == AppsStatus.Default)
211                 {
212                     MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.LongPress.ToString());
213                 }
214             });
215
216             AppList.OnChangeFocusChainingCommand = new Command(() =>
217             {
218                 MakeFocusChaining();
219                 AppList.InitializeFocus();
220             });
221         }
222
223         protected override async void OnAppearing()
224         {
225             base.OnAppearing();
226
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;
233             await Task.Delay(1);
234             AppList.InitializeFocus();
235             MakeFocusChaining();
236
237             pinnedAppCount = SumOfCheckedApp;
238         }
239
240         private void MakeFocusChaining()
241         {
242             List<View> upperList = AppList.GetAppsUpperList().ToList();
243             List<View> lowerList = AppList.GetAppsLowerList().ToList();
244
245             int upperCount = upperList.Count;
246             int lowerCount = lowerList.Count;
247
248             Button prevButton;
249             Button button;
250             Button nextButton;
251
252             for (int i = 1; i < upperCount-1; i++)
253             {
254                 prevButton = upperList[i-1].FindByName<Button>("ButtonFocusArea");
255                 button = upperList[i].FindByName<Button>("ButtonFocusArea");
256                 nextButton = upperList[i + 1].FindByName<Button>("ButtonFocusArea");
257
258                 SetFocusChainingLeftAndRight(prevButton, button);
259                 SetFocusChainingLeftAndRight(button, nextButton);
260
261                 if (i == 1)
262                 {
263                     prevButton?.On<Tizen>()?.SetNextFocusDownView(
264                         lowerList[i - 1].FindByName<Button>("ButtonFocusArea"));
265                 }
266
267                 button?.On<Tizen>()?.SetNextFocusDownView(
268                     lowerList[i].FindByName<Button>("ButtonFocusArea"));
269
270                 if (i == upperCount-2)
271                 {
272                     button?.On<Tizen>()?.SetNextFocusDownView(lowerList[i].FindByName<Button>("ButtonFocusArea"));
273
274                     nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
275                     nextButton?.On<Tizen>()?.SetNextFocusDownView(
276                         lowerList[lowerCount - 1].FindByName<Button>("ButtonFocusArea"));
277                 }
278             }
279
280             for (int i = 1; i < lowerCount - 1; i++)
281             {
282                 prevButton = lowerList[i - 1].FindByName<Button>("ButtonFocusArea");
283                 button = lowerList[i].FindByName<Button>("ButtonFocusArea");
284                 nextButton = lowerList[i + 1].FindByName<Button>("ButtonFocusArea");
285
286                 SetFocusChainingLeftAndRight(prevButton, button);
287                 SetFocusChainingLeftAndRight(button, nextButton);
288
289                 if (i == 1)
290                 {
291                     prevButton?.On<Tizen>()?.SetNextFocusUpView(
292                         upperList[i - 1].FindByName<Button>("ButtonFocusArea"));
293                 }
294
295                 button?.On<Tizen>()?.SetNextFocusUpView(
296                     upperList[i].FindByName<Button>("ButtonFocusArea"));
297
298                 if (i == lowerCount-2)
299                 {
300                     nextButton?.On<Tizen>()?.SetNextFocusUpView(upperList[i+1].FindByName<Button>("ButtonFocusArea"));
301
302                     if (upperCount > lowerCount)
303                     {
304                         nextButton?.On<Tizen>()?.SetNextFocusRightView(upperList[upperCount-1].FindByName<Button>("ButtonFocusArea"));
305                     }
306                     else
307                     {
308                         nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
309                     }
310                 }
311             }
312
313             SetFocusChainingWithCurrentStatus(CurrentStatus);
314         }
315
316         private void SetFocusChainingLeftAndRight(Button left, Button right)
317         {
318             left?.On<Tizen>()?.SetNextFocusRightView(right);
319             right?.On<Tizen>()?.SetNextFocusLeftView(left);
320         }
321
322         private void SetFocusChainingUpAndDownForDefaultMode()
323         {
324             List<View> lowerList = AppList.GetAppsLowerList().ToList();
325             sortList = FooterNormal.GetSortDropdownList();
326
327             foreach (var item in lowerList)
328             {
329                 Button button = item.FindByName<Button>("ButtonFocusArea");
330                 button?.On<Tizen>()?.SetNextFocusDownView(sortList);
331             }
332         }
333
334         private void SetFocusChainingUpAndDownForDeleteMode()
335         {
336             List<View> lowerList = AppList.GetAppsLowerList().ToList();
337             cancelButton = FooterDelete.GetCancelButton();
338
339             foreach (var item in lowerList)
340             {
341                 Button button = item.FindByName<Button>("ButtonFocusArea");
342                 button?.On<Tizen>()?.SetNextFocusDownView(cancelButton);
343             }
344
345             if (lowerList.Count > 0)
346             {
347                 cancelButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
348             }
349
350             cancelButton?.On<Tizen>()?.SetNextFocusLeftView(cancelButton);
351         }
352
353         private void SetFocusChainingUpAndDownForPinMode()
354         {
355             List<View> lowerList = AppList.GetAppsLowerList().ToList();
356             doneButton = FooterPin.GetDoneButton();
357
358             foreach (var item in lowerList)
359             {
360                 Button button = item.FindByName<Button>("ButtonFocusArea");
361                 button?.On<Tizen>()?.SetNextFocusDownView(doneButton);
362             }
363
364             if (lowerList.Count > 0 )
365             {
366                 doneButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
367             }
368
369             doneButton?.On<Tizen>()?.SetNextFocusLeftView(doneButton);
370         }
371
372         private void SetFocusChainingWithCurrentStatus(AppsStatus status)
373         {
374             switch (status)
375             {
376                 case AppsStatus.Default:
377                     SetFocusChainingUpAndDownForDefaultMode();
378                     break;
379                 case AppsStatus.Pin:
380                     SetFocusChainingUpAndDownForPinMode();
381                     break;
382                 case AppsStatus.Delete:
383                     SetFocusChainingUpAndDownForDeleteMode();
384                     break;
385                 case AppsStatus.LongPress:
386                     SetFocusChainingUpAndDownForDefaultMode();
387                     break;
388             }
389         }
390
391         /// <summary>
392         /// A method sets current status of MainPage
393         /// Changes visibility of footers
394         /// </summary>
395         /// <param name="status">The next status name</param>
396         private void SetCurrentStatus(AppsStatus status)
397         {
398             switch (status)
399             {
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();
408                     break;
409                 case AppsStatus.Pin:
410                     FooterNormal.IsVisible = false;
411                     FooterPin.IsVisible = true;
412                     FooterDelete.IsVisible = false;
413                     BackKeyInfo.Text = "Front";
414                     break;
415                 case AppsStatus.Delete:
416                     FooterNormal.IsVisible = false;
417                     FooterPin.IsVisible = false;
418                     FooterDelete.IsVisible = true;
419                     BackKeyInfo.Text = "Front";
420                     break;
421                 case AppsStatus.LongPress:
422                     FooterNormal.IsVisible = true;
423                     FooterPin.IsVisible = false;
424                     FooterDelete.IsVisible = false;
425                     BackKeyInfo.Text = "Front";
426                     break;
427             }
428
429             MakeFocusChaining();
430         }
431
432         /// <summary>
433         /// This method is called when the properties of MainPage is changed
434         /// If CurrentStatus is changed, call SetCurrentStatus method
435         /// </summary>
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)
439         {
440             if (e.PropertyName.Equals("CurrentStatus"))
441             {
442                 SetCurrentStatus(CurrentStatus);
443             }
444             else if (e.PropertyName.Equals("IsEnabledDeletePopup"))
445             {
446                 if (IsEnabledDeletePopup)
447                 {
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);
453                 }
454                 else
455                 {
456                     AppList.InitializeFocus();
457                 }
458             }
459             else if (e.PropertyName.Equals("SumOfCheckedApp"))
460             {
461                 if (CurrentStatus == AppsStatus.Pin)
462                 {
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)
469                     {
470                         AdditionalInfoText1.Text = UnpinnedAppName;
471                         AdditionalInfoText2.Text = "Unpinned";
472                     }
473                     else
474                     {
475                         AdditionalInfoText1.Text = PinnedAppName;
476                         AdditionalInfoText2.Text = "Pinned";
477                     }
478
479                     pinnedAppCount = SumOfCheckedApp;
480                 }
481             }
482         }
483
484         /// <summary>
485         /// A task for handling BackKey event
486         /// </summary>
487         /// <returns>Always returns true</returns>
488         private async Task<bool> OnBackKeyPressedAtMain()
489         {
490             if (CurrentStatus != AppsStatus.Default)
491             {
492                 MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.Default.ToString());
493                 return true;
494             }
495
496             if (!AppList.IsFirstItemFocused)
497             {
498                 AppList.InitializeFocus();
499                 return true;
500             }
501
502             var answer = await DisplayAlert("QUIT", "Do you want to quit?", "YES", "NO");
503             if (answer)
504             {
505                 PlayHideAnimation();
506                 await Task.Delay(800);
507                 AppControlUtils.SelfTerminate();
508             }
509             else
510             {
511                 AppList.InitializeFocus();
512             }
513
514             return true;
515         }
516
517         /// <summary>
518         /// This method is called when Back button is pressed
519         /// </summary>
520         /// <returns>Always returns true</returns>
521         /// <see cref="Page.OnBackButtonPressed"/>
522         protected override bool OnBackButtonPressed()
523         {
524             if (IsPinAppRequested)
525             {
526                 return false;
527             }
528
529             SynchronizationContext.Current.Post(async (o) =>
530             {
531                 await OnBackKeyPressedAtMain();
532             }, "");
533             return true;
534         }
535     }
536 }