Implements Apps out VI
[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
43         /// <summary>
44         /// SubPanel icon's transition height value when it focused.
45         /// </summary>
46         private int showTransitionHeight = SizeUtils.GetHeightSize(12);
47
48         /// <summary>
49         /// Identifies the CurrentStatus bindable property
50         /// </summary>
51         public static readonly BindableProperty CurrentStatusProperty = BindableProperty.Create("CurrentStatus", typeof(AppsStatus), typeof(MainPage), default(AppsStatus));
52
53         /// <summary>
54         /// Gets or sets current status of MainPage
55         /// </summary>
56         public AppsStatus CurrentStatus
57         {
58             get { return (AppsStatus)GetValue(CurrentStatusProperty); }
59             set { SetValue(CurrentStatusProperty, value); }
60         }
61
62         /// <summary>
63         /// Identifies the app label bindable property
64         /// </summary>
65         public static readonly BindableProperty DeletePopupAppLabelProperty = BindableProperty.Create("DeletePopupAppLabel", typeof(string), typeof(MainPage), default(string));
66
67         /// <summary>
68         /// Gets or sets the app label to display at the delete popup
69         /// </summary>
70         public string DeletePopupAppLabel
71         {
72             get { return (string)GetValue(DeletePopupAppLabelProperty); }
73             set { SetValue(DeletePopupAppLabelProperty, value); }
74         }
75
76         /// <summary>
77         /// Identifies the delete popup visibility bindable property
78         /// </summary>
79         public static readonly BindableProperty IsEnabledDeletePopupProperty = BindableProperty.Create("IsEnabledDeletePopup", typeof(bool), typeof(MainPage), default(bool));
80
81         /// <summary>
82         /// Gets or sets whether delete popup is displayed
83         /// </summary>
84         public bool IsEnabledDeletePopup
85         {
86             get { return (bool)GetValue(IsEnabledDeletePopupProperty); }
87             set { SetValue(IsEnabledDeletePopupProperty, value); }
88         }
89
90         /// <summary>
91         /// Identifies the pin app control request received information bindable property
92         /// </summary>
93         public static readonly BindableProperty IsPinAppRequestedProperty = BindableProperty.Create("IsPinAppRequested", typeof(bool), typeof(MainPage), default(bool));
94
95         /// <summary>
96         /// Gets or sets whether pin app control request received information.
97         /// </summary>
98         public bool IsPinAppRequested
99         {
100             get { return (bool)GetValue(IsPinAppRequestedProperty); }
101             set { SetValue(IsPinAppRequestedProperty, value); }
102         }
103
104         /// <summary>
105         /// Identifies the delete popup command bindable property
106         /// </summary>
107         public static readonly BindableProperty DeletePopupCommandProperty = BindableProperty.Create("DeletePopupCommand", typeof(Command), typeof(MainPage), default(Command));
108
109         /// <summary>
110         /// A command will be executed if the button of the delete popup is clicked
111         /// </summary>
112         public ICommand DeletePopupCommand
113         {
114             get { return (ICommand)GetValue(DeletePopupCommandProperty); }
115             set { SetValue(DeletePopupCommandProperty, value); }
116         }
117
118         /// <summary>
119         /// Identifies the SumOfCheckedApp bindable property
120         /// </summary>
121         public static readonly BindableProperty SumOfCheckedAppProperty = BindableProperty.Create("SumOfCheckedApp", typeof(int), typeof(MainPage), default(int));
122
123         /// <summary>
124         /// Gets or sets count of checked AppItemCell
125         /// </summary>
126         public int SumOfCheckedApp
127         {
128             get { return (int)GetValue(SumOfCheckedAppProperty); }
129             set { SetValue(SumOfCheckedAppProperty, value); }
130         }
131
132
133         private async void PlayHideAnimation()
134         {
135             var animation = new Animation();
136             var headerAnimation = new Animation(v => Header.Opacity = v, 1, 0);
137             var titleAnimation = new Animation(v => TitleLabel.Opacity = v, 1, 0);
138             var footerAnimation = new Animation(v => FooterNormal.Opacity = v, 1, 0);
139             var appListHeightAnimation = new Animation(v => AppList.TranslationY = v, 0, SizeUtils.GetHeightSize(12));
140             var appListAnimation = new Animation(v => AppList.Opacity = v, 1, 0);
141             var mainAnimation = new Animation(v => this.Opacity = v, 1, 0);
142
143             animation.Add(0.2, 0.8, headerAnimation);
144             animation.Add(0.2, 0.8, titleAnimation);
145             animation.Add(0.2, 0.8, footerAnimation);
146             animation.Add(0.2, 1, appListHeightAnimation);
147             animation.Add(0.2, 0.8, appListAnimation);
148             animation.Add(0.2, 1, mainAnimation);
149
150             animation.Commit(this, "QuitAnimation", 16, 834);
151         }
152
153
154         /// <summary>
155         /// A constructor
156         /// Adds PropertyChanged event handler and MenuKey event listener
157         /// </summary>
158         public MainPage()
159         {
160             InitializeComponent();
161
162             int backKeyImageSize = SizeUtils.GetHeightSize(40);
163             BackKeyInfoImage.WidthRequest = backKeyImageSize;
164             BackKeyInfoImage.HeightRequest = backKeyImageSize;
165             BackKeyInfo.FontSize = SizeUtils.GetFontSize(28);
166             BackKeyInfo.Margin = new Thickness(SizeUtils.GetWidthSize(6), 0, 0, 0);
167
168
169             PropertyChanged += MainPagePropertyChanged;
170             SetCurrentStatus(AppsStatus.Default);
171
172             // TODO: This code is temporary for menu option test
173             App.SetMenuKeyListener((e, arg) =>
174             {
175                 DebuggingUtils.Dbg("[Apps] Menu key is pressed");
176
177                 if (CurrentStatus == AppsStatus.Default)
178                 {
179                     MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.LongPress.ToString());
180                 }
181             });
182
183             AppList.OnChangeFocusChainingCommand = new Command(() =>
184             {
185                 MakeFocusChaining();
186                 AppList.InitializeFocus();
187             });
188         }
189
190         protected override async void OnAppearing()
191         {
192             base.OnAppearing();
193
194             await AppList.TranslateTo(0, showTransitionHeight, 0);
195 #pragma warning disable CS4014
196             AppList.TranslateTo(0, 0, 667);
197 #pragma warning restore CS4014
198             await PageDimBox.FadeTo(0.0, 667);
199             PageDimBox.IsVisible = false;
200             await Task.Delay(1);
201             AppList.InitializeFocus();
202             MakeFocusChaining();
203         }
204
205         private void MakeFocusChaining()
206         {
207             List<View> upperList = AppList.GetAppsUpperList().ToList();
208             List<View> lowerList = AppList.GetAppsLowerList().ToList();
209
210             int upperCount = upperList.Count;
211             int lowerCount = lowerList.Count;
212
213             Button prevButton;
214             Button button;
215             Button nextButton;
216
217             for (int i = 1; i < upperCount-1; i++)
218             {
219                 prevButton = upperList[i-1].FindByName<Button>("ButtonFocusArea");
220                 button = upperList[i].FindByName<Button>("ButtonFocusArea");
221                 nextButton = upperList[i + 1].FindByName<Button>("ButtonFocusArea");
222
223                 SetFocusChainingLeftAndRight(prevButton, button);
224                 SetFocusChainingLeftAndRight(button, nextButton);
225
226                 if (i == 1)
227                 {
228                     prevButton?.On<Tizen>()?.SetNextFocusDownView(
229                         lowerList[i - 1].FindByName<Button>("ButtonFocusArea"));
230                 }
231
232                 button?.On<Tizen>()?.SetNextFocusDownView(
233                     lowerList[i].FindByName<Button>("ButtonFocusArea"));
234
235                 if (i == upperCount-2)
236                 {
237                     button?.On<Tizen>()?.SetNextFocusDownView(lowerList[i].FindByName<Button>("ButtonFocusArea"));
238
239                     nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
240                     nextButton?.On<Tizen>()?.SetNextFocusDownView(
241                         lowerList[lowerCount - 1].FindByName<Button>("ButtonFocusArea"));
242                 }
243             }
244
245             for (int i = 1; i < lowerCount - 1; i++)
246             {
247                 prevButton = lowerList[i - 1].FindByName<Button>("ButtonFocusArea");
248                 button = lowerList[i].FindByName<Button>("ButtonFocusArea");
249                 nextButton = lowerList[i + 1].FindByName<Button>("ButtonFocusArea");
250
251                 SetFocusChainingLeftAndRight(prevButton, button);
252                 SetFocusChainingLeftAndRight(button, nextButton);
253
254                 if (i == 1)
255                 {
256                     prevButton?.On<Tizen>()?.SetNextFocusUpView(
257                         upperList[i - 1].FindByName<Button>("ButtonFocusArea"));
258                 }
259
260                 button?.On<Tizen>()?.SetNextFocusUpView(
261                     upperList[i].FindByName<Button>("ButtonFocusArea"));
262
263                 if (i == lowerCount-2)
264                 {
265                     nextButton?.On<Tizen>()?.SetNextFocusUpView(upperList[i+1].FindByName<Button>("ButtonFocusArea"));
266
267                     if (upperCount > lowerCount)
268                     {
269                         nextButton?.On<Tizen>()?.SetNextFocusRightView(upperList[upperCount-1].FindByName<Button>("ButtonFocusArea"));
270                     }
271                     else
272                     {
273                         nextButton?.On<Tizen>()?.SetNextFocusRightView(nextButton);
274                     }
275                 }
276             }
277
278             SetFocusChainingWithCurrentStatus(CurrentStatus);
279         }
280
281         private void SetFocusChainingLeftAndRight(Button left, Button right)
282         {
283             left?.On<Tizen>()?.SetNextFocusRightView(right);
284             right?.On<Tizen>()?.SetNextFocusLeftView(left);
285         }
286
287         private void SetFocusChainingUpAndDownForDefaultMode()
288         {
289             List<View> lowerList = AppList.GetAppsLowerList().ToList();
290             sortList = FooterNormal.GetSortDropdownList();
291
292             foreach (var item in lowerList)
293             {
294                 Button button = item.FindByName<Button>("ButtonFocusArea");
295                 button?.On<Tizen>()?.SetNextFocusDownView(sortList);
296             }
297         }
298
299         private void SetFocusChainingUpAndDownForDeleteMode()
300         {
301             List<View> lowerList = AppList.GetAppsLowerList().ToList();
302             cancelButton = FooterDelete.GetCancelButton();
303
304             foreach (var item in lowerList)
305             {
306                 Button button = item.FindByName<Button>("ButtonFocusArea");
307                 button?.On<Tizen>()?.SetNextFocusDownView(cancelButton);
308             }
309
310             if (lowerList.Count > 0)
311             {
312                 cancelButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
313             }
314
315             cancelButton?.On<Tizen>()?.SetNextFocusLeftView(cancelButton);
316         }
317
318         private void SetFocusChainingUpAndDownForPinMode()
319         {
320             List<View> lowerList = AppList.GetAppsLowerList().ToList();
321             doneButton = FooterPin.GetDoneButton();
322
323             foreach (var item in lowerList)
324             {
325                 Button button = item.FindByName<Button>("ButtonFocusArea");
326                 button?.On<Tizen>()?.SetNextFocusDownView(doneButton);
327             }
328
329             if (lowerList.Count > 0 )
330             {
331                 doneButton?.On<Tizen>()?.SetNextFocusUpView(lowerList[lowerList.Count - 1].FindByName<Button>("ButtonFocusArea"));
332             }
333
334             doneButton?.On<Tizen>()?.SetNextFocusLeftView(doneButton);
335         }
336
337         private void SetFocusChainingWithCurrentStatus(AppsStatus status)
338         {
339             switch (status)
340             {
341                 case AppsStatus.Default:
342                     SetFocusChainingUpAndDownForDefaultMode();
343                     break;
344                 case AppsStatus.Pin:
345                     SetFocusChainingUpAndDownForPinMode();
346                     break;
347                 case AppsStatus.Delete:
348                     SetFocusChainingUpAndDownForDeleteMode();
349                     break;
350                 case AppsStatus.LongPress:
351                     SetFocusChainingUpAndDownForDefaultMode();
352                     break;
353             }
354         }
355
356         /// <summary>
357         /// A method sets current status of MainPage
358         /// Changes visibility of footers
359         /// </summary>
360         /// <param name="status">The next status name</param>
361         private void SetCurrentStatus(AppsStatus status)
362         {
363             switch (status)
364             {
365                 case AppsStatus.Default:
366                     FooterNormal.IsVisible = true;
367                     FooterPin.IsVisible = false;
368                     FooterDelete.IsVisible = false;
369                     BackKeyInfo.Text = "Quit";
370                     AddtionalInfo.IsVisible = false;
371                     TitleLabel.IsVisible = true;
372                     AppList.InitializeFocus();
373                     break;
374                 case AppsStatus.Pin:
375                     FooterNormal.IsVisible = false;
376                     FooterPin.IsVisible = true;
377                     FooterDelete.IsVisible = false;
378                     BackKeyInfo.Text = "Front";
379                     break;
380                 case AppsStatus.Delete:
381                     FooterNormal.IsVisible = false;
382                     FooterPin.IsVisible = false;
383                     FooterDelete.IsVisible = true;
384                     BackKeyInfo.Text = "Front";
385                     break;
386                 case AppsStatus.LongPress:
387                     FooterNormal.IsVisible = true;
388                     FooterPin.IsVisible = false;
389                     FooterDelete.IsVisible = false;
390                     BackKeyInfo.Text = "Front";
391                     break;
392             }
393
394             MakeFocusChaining();
395         }
396
397         /// <summary>
398         /// This method is called when the properties of MainPage is changed
399         /// If CurrentStatus is changed, call SetCurrentStatus method
400         /// </summary>
401         /// <param name="sender">The source of the event</param>
402         /// <param name="e">The event that is occurred when property of MainPage is changed</param>
403         private async void MainPagePropertyChanged(object sender, PropertyChangedEventArgs e)
404         {
405             if (e.PropertyName.Equals("CurrentStatus"))
406             {
407                 SetCurrentStatus(CurrentStatus);
408             }
409             else if (e.PropertyName.Equals("IsEnabledDeletePopup"))
410             {
411                 if (IsEnabledDeletePopup)
412                 {
413                     bool answer = await DisplayAlert(DeletePopupAppLabel, "Do you want to delete?", "YES", "NO");
414                     Dictionary<string, string> ret = new Dictionary<string, string>();
415                     ret.Add("AppLabel", DeletePopupAppLabel);
416                     ret.Add("answer", answer ? "yes" : "no");
417                     DeletePopupCommand?.Execute(ret);
418                 }
419                 else
420                 {
421                     AppList.InitializeFocus();
422                 }
423             }
424             else if (e.PropertyName.Equals("SumOfCheckedApp"))
425             {
426                 AdditionalInfoText1.Text = SumOfCheckedApp.ToString() + " Apps";
427                 AdditionalInfoText2.Text = "Checked";
428             }
429         }
430
431         /// <summary>
432         /// A task for handling BackKey event
433         /// </summary>
434         /// <returns>Always returns true</returns>
435         private async Task<bool> OnBackKeyPressedAtMain()
436         {
437             if (CurrentStatus != AppsStatus.Default)
438             {
439                 MessagingCenter.Send<MainPage, string>(this, "ChangeCurrentStatus", AppsStatus.Default.ToString());
440                 return true;
441             }
442
443             if (!AppList.IsFirstItemFocused)
444             {
445                 AppList.InitializeFocus();
446                 return true;
447             }
448
449             var answer = await DisplayAlert("QUIT", "Do you want to quit?", "YES", "NO");
450             if (answer)
451             {
452                 PlayHideAnimation();
453                 await Task.Delay(800);
454                 AppControlUtils.SelfTerminate();
455             }
456             else
457             {
458                 AppList.InitializeFocus();
459             }
460
461             return true;
462         }
463
464         /// <summary>
465         /// This method is called when Back button is pressed
466         /// </summary>
467         /// <returns>Always returns true</returns>
468         /// <see cref="Page.OnBackButtonPressed"/>
469         protected override bool OnBackButtonPressed()
470         {
471             if (IsPinAppRequested)
472             {
473                 return false;
474             }
475
476             SynchronizationContext.Current.Post(async (o) =>
477             {
478                 await OnBackKeyPressedAtMain();
479             }, "");
480             return true;
481         }
482     }
483 }