Add exception handling in the image viewer zoom mode
[profile/tv/apps/dotnet/mediahub.git] / TVMediaHub / TVMediaHub.Tizen / Views / ImageViewerZoom.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 TVMediaHub.Tizen.ViewModels;
18 using Xamarin.Forms;
19 using Tizen.Xamarin.Forms.Extension;
20 using TVMediaHub.Tizen.Utils;
21 using System.IO;
22 using Tizen;
23 using TVMediaHub.Tizen.Controls;
24 using System.Collections.Generic;
25 using System;
26
27 namespace TVMediaHub.Tizen.Views
28 {
29     public partial class ImageViewerZoom : ContentPageEx
30     {
31         private static List<string> MoveDirection = new List<string> { "Up", "Down", "Left", "Right" };
32
33         //TODO: need to check the move size
34         private static int clipperMoveGap = 10;
35         private static double zoomRatio = 1.5;
36         private ElmSharp.ImageOrientation imgOrientation = ElmSharp.ImageOrientation.None;
37
38         private ElmSharp.Rectangle backgroundThumbnail;
39         private ElmSharp.Image thumbnailImg;
40         private ElmSharp.Image thumbnailDim;
41         private ElmSharp.Image clippingImg;
42         private ElmSharp.Rectangle clipper;
43         private ElmSharp.Image clipperBorder;
44
45         public static readonly BindableProperty SavedRotationProperty = BindableProperty.Create("SavedRotation", typeof(double), typeof(ImageViewerZoom), default(double));
46         public double SavedRotation
47         {
48             get { return (double)GetValue(SavedRotationProperty); }
49             set { SetValue(SavedRotationProperty, value); }
50         }
51
52         public static readonly BindableProperty ThumbnailPathProperty = BindableProperty.Create("ThumbnailPath", typeof(string), typeof(ImageViewerZoom), default(string));
53         public string ThumbnailPath
54         {
55             get { return (string)GetValue(ThumbnailPathProperty); }
56             set { SetValue(ThumbnailPathProperty, value); }
57         }
58
59         public ImageViewerZoom()
60         {
61             BindingContext = ImageTabViewModelLocator.ViewModel;
62             InitializeComponent();
63
64             int BackKeyInfoLabelSize = SizeUtils.GetFontSize(28);
65             BackKeyInfoLabel.FontSize = BackKeyInfoLabelSize;
66             Toast.DisplayText("Press [Back] to Escape from Zoom mode.");
67
68             /// Set the current image
69             setCurrentImage();
70
71             /// Create the mini map
72             createMiniMap();
73
74             OnBackButtonPressedHandler += (s, e) =>
75             {
76                 Program.TransitionTo(new SimpleImageViewer());
77             };
78         }
79
80         protected override void InitializePage()
81         {
82             /// Grabs the hw keys to move the given image
83             grabMoveKeys();
84         }
85
86         protected override void FinalizePage()
87         {
88             ungrabMoveKeys();
89
90             if (backgroundThumbnail != null)
91             {
92                 backgroundThumbnail.Unrealize();
93                 backgroundThumbnail = null;
94             }
95
96             if (thumbnailImg != null)
97             {
98                 thumbnailImg.Unrealize();
99                 thumbnailImg = null;
100             }
101
102             if (thumbnailDim != null)
103             {
104                 thumbnailDim.Unrealize();
105                 thumbnailDim = null;
106             }
107
108             if (clippingImg != null)
109             {
110                 clippingImg.Unrealize();
111                 clippingImg = null;
112             }
113
114             if (clipper != null)
115             {
116                 clipper.Unrealize();
117                 clipper = null;
118             }
119
120             if (clipperBorder != null)
121             {
122                 clipperBorder.Unrealize();
123                 clipperBorder = null;
124             }
125         }
126
127         /// <summary>
128         /// Grabs the hw keys to move the given image
129         /// </summary>
130         private void grabMoveKeys()
131         {
132             foreach (string direction in MoveDirection)
133             {
134                 App.MainWindow.KeyGrab(direction, false);
135             }
136
137             App.MainWindow.KeyUp += MoveKeyUpListener;
138             App.MainWindow.KeyDown += MoveKeyDownListener;
139         }
140
141         /// <summary>
142         /// Ungrab the hw keys
143         /// </summary>
144         private void ungrabMoveKeys()
145         {
146             App.MainWindow.KeyUp -= MoveKeyUpListener;
147             App.MainWindow.KeyDown -= MoveKeyDownListener;
148
149             foreach (string direction in MoveDirection)
150             {
151                 App.MainWindow.KeyUngrab(direction);
152             }
153         }
154
155         private void MoveKeyUpListener(object sender, ElmSharp.EvasKeyEventArgs e)
156         {
157             DbgPort.D("[ZoomMode] key up : " + e.KeyName);
158
159             if (!MoveDirection.Contains(e.KeyName))
160             {
161                 return;
162             }
163
164             int direction = MoveDirection.FindIndex(x => x.Equals(e.KeyName));
165             object[] buttons = new object[4] { UpButton, DownButton, LeftButton, RightButton };
166             (buttons[direction] as CustomImageButton).OnButtonReleased();
167         }
168
169         private void MoveKeyDownListener(object sender, ElmSharp.EvasKeyEventArgs e)
170         {
171             DbgPort.D("[ZoomMode] key down : " + e.KeyName);
172
173             if (!MoveDirection.Contains(e.KeyName))
174             {
175                 return;
176             }
177
178             MoveCurrentImage(e.KeyName);
179
180             int direction = MoveDirection.FindIndex(x => x.Equals(e.KeyName));
181             object[] buttons = new object[4] { UpButton, DownButton, LeftButton, RightButton };
182             (buttons[direction] as CustomImageButton).OnButtonPressed();
183         }
184
185         private void MoveCurrentImage(string direction)
186         {
187             int moveDuration = 350;
188             if (direction.Equals("Up"))
189             {
190                 int toY = clipper.Geometry.Y - clipperMoveGap;
191
192                 if (toY <= thumbnailDim.Geometry.Y)
193                 {
194                     clipper.Move(clipper.Geometry.X, thumbnailDim.Geometry.Y);
195                     CurrentImage.TranslateTo(CurrentImage.TranslationX, CurrentImage.Y, Convert.ToUInt32(moveDuration), Easing.CubicOut);
196
197                     return;
198                 }
199
200                 ElmSharp.EcoreAnimator.AddAmimator(delegate ()
201                 {
202                     clipper.Move(clipper.Geometry.X, clipper.Geometry.Y - 1);
203                     if (clipper.Geometry.Y <= toY)
204                     {
205                         clipper.Move(clipper.Geometry.X, toY);
206
207                         return false;
208                     }
209
210                     return true;
211                 });
212
213                 CurrentImage.TranslateTo(CurrentImage.TranslationX, CurrentImage.TranslationY + clipperMoveGap * (CurrentImage.Height / thumbnailDim.Geometry.Height), Convert.ToUInt32(moveDuration), Easing.CubicOut);
214             }
215             else if (direction.Equals("Down"))
216             {
217                 int toY = clipper.Geometry.Y + clipperMoveGap;
218
219                 if ((toY + clipper.Geometry.Height) >= (thumbnailDim.Geometry.Y + thumbnailDim.Geometry.Height))
220                 {
221                     clipper.Move(clipper.Geometry.X, (thumbnailDim.Geometry.Y + thumbnailDim.Geometry.Height) - clipper.Geometry.Height);
222                     CurrentImage.TranslateTo(CurrentImage.TranslationX, -(CurrentImage.Height - App.MainWindow.Geometry.Height), Convert.ToUInt32(moveDuration), Easing.CubicOut);
223
224                     return;
225                 }
226
227                 ElmSharp.EcoreAnimator.AddAmimator(delegate ()
228                 {
229                     clipper.Move(clipper.Geometry.X, clipper.Geometry.Y + 1);
230                     if (clipper.Geometry.Y >= toY)
231                     {
232                         clipper.Move(clipper.Geometry.X, toY);
233
234                         return false;
235                     }
236
237                     return true;
238                 });
239
240                 CurrentImage.TranslateTo(CurrentImage.TranslationX, CurrentImage.TranslationY - clipperMoveGap * (CurrentImage.Height / thumbnailDim.Geometry.Height), Convert.ToUInt32(moveDuration), Easing.CubicOut);
241             }
242             else if (direction.Equals("Left"))
243             {
244                 int toX = clipper.Geometry.X - clipperMoveGap;
245
246                 if (toX <= thumbnailDim.Geometry.X)
247                 {
248                     clipper.Move(thumbnailDim.Geometry.X, clipper.Geometry.Y);
249                     CurrentImage.TranslateTo(CurrentImage.X, CurrentImage.TranslationY, Convert.ToUInt32(moveDuration), Easing.CubicOut);
250
251                     return;
252                 }
253
254                 ElmSharp.EcoreAnimator.AddAmimator(delegate ()
255                 {
256                     clipper.Move(clipper.Geometry.X - 1, clipper.Geometry.Y);
257                     if (clipper.Geometry.X <= toX)
258                     {
259                         clipper.Move(toX, clipper.Geometry.Y);
260
261                         return false;
262                     }
263
264                     return true;
265                 });
266
267                 CurrentImage.TranslateTo(CurrentImage.TranslationX + clipperMoveGap * (CurrentImage.Width / thumbnailDim.Geometry.Width), CurrentImage.TranslationY, Convert.ToUInt32(moveDuration), Easing.CubicOut);
268             }
269             else if (direction.Equals("Right"))
270             {
271                 int toX = clipper.Geometry.X + clipperMoveGap;
272                 if ((toX + clipper.Geometry.Width) >= (thumbnailDim.Geometry.X + thumbnailDim.Geometry.Width))
273                 {
274                     clipper.Move((thumbnailDim.Geometry.X + thumbnailDim.Geometry.Width) - clipper.Geometry.Width, clipper.Geometry.Y);
275                     CurrentImage.TranslateTo(-(CurrentImage.Width - App.MainWindow.Geometry.Width), CurrentImage.TranslationY, Convert.ToUInt32(moveDuration), Easing.CubicOut);
276
277                     return;
278                 }
279
280                 ElmSharp.EcoreAnimator.AddAmimator(delegate ()
281                 {
282                     clipper.Move(clipper.Geometry.X + 1, clipper.Geometry.Y);
283                     if (clipper.Geometry.X >= toX)
284                     {
285                         clipper.Move(toX, clipper.Geometry.Y);
286
287                         return false;
288                     }
289
290                     return true;
291                 });
292
293                 CurrentImage.TranslateTo(CurrentImage.TranslationX - clipperMoveGap * (CurrentImage.Width / thumbnailDim.Geometry.Width), CurrentImage.TranslationY, Convert.ToUInt32(moveDuration), Easing.CubicOut);
294             }
295             else
296             {
297                 DbgPort.D("Wrong direction : " + direction);
298                 return;
299             }
300         }
301
302         private void setCurrentImage()
303         {
304             CurrentImage.WidthRequest = App.MainWindow.Geometry.Width * zoomRatio;
305             CurrentImage.HeightRequest = App.MainWindow.Geometry.Height * zoomRatio;
306             CurrentImage.TranslationX = -((App.MainWindow.Geometry.Width * zoomRatio) - App.MainWindow.Geometry.Width) / 2;
307             CurrentImage.TranslationY = -((App.MainWindow.Geometry.Height * zoomRatio) - App.MainWindow.Geometry.Height) / 2;
308
309             int currentAngle = Convert.ToInt32(SavedRotation);
310             imgOrientation = (ElmSharp.ImageOrientation)Enum.ToObject(typeof(ElmSharp.ImageOrientation), currentAngle / 90);
311
312             CurrentImage.Scale = (currentAngle == 90 || currentAngle == 270) ? (double)((double)App.MainWindow.Geometry.Height / (double)App.MainWindow.Geometry.Width) : 1;
313             CurrentImage.Aspect = (currentAngle == 90 || currentAngle == 270) ? Aspect.AspectFill : Aspect.AspectFit;
314             CurrentImage.Rotation = SavedRotation;
315         }
316
317         private void createMiniMap()
318         {
319             Grid miniMap = new Grid()
320             {
321                 WidthRequest = 640,
322                 HeightRequest = 430,
323                 Padding = 0,
324             };
325             miniMap.RowDefinitions.Add(new RowDefinition { Height = new GridLength(430) });
326             miniMap.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(640) });
327             ZoomLayout.Children.Add(miniMap,
328                 Constraint.Constant(App.MainWindow.Geometry.Width - 96 - 480 - 80), /// X
329                 Constraint.Constant(App.MainWindow.Geometry.Height - 96 - 270 - 80), /// Y
330                 Constraint.Constant(640), /// Width
331                 Constraint.Constant(430)); /// Height
332
333             /// create the shadow of the mini map
334             var backgroundShadow = new NinePatch
335             {
336                 Source = "img_zoom_minimap_shadow.9.png",
337                 WidthRequest = miniMap.Width,
338                 HeightRequest = miniMap.Height,
339                 BorderTop = 119,
340                 BorderBottom = 119,
341                 BorderLeft = 119,
342                 BorderRight = 119,
343                 Aspect = Aspect.Fill,
344             };
345             miniMap.Children.Add(backgroundShadow, 0, 0);
346
347             Grid thumbnailFrame = new Grid();
348             thumbnailFrame.RowDefinitions.Add(new RowDefinition { Height = new GridLength(80) });
349             thumbnailFrame.RowDefinitions.Add(new RowDefinition { Height = new GridLength(270) });
350             thumbnailFrame.RowDefinitions.Add(new RowDefinition { Height = new GridLength(80) });
351             thumbnailFrame.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
352             thumbnailFrame.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(480) });
353             thumbnailFrame.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
354             miniMap.Children.Add(thumbnailFrame, 0, 0);
355
356             RelativeLayout thumbnailMap = new RelativeLayout();
357             thumbnailFrame.Children.Add(thumbnailMap, 1, 1);
358
359             /// Using Elm-Sharp
360             backgroundThumbnail = new ElmSharp.Rectangle(App.MainWindow);
361             backgroundThumbnail.Color = new ElmSharp.Color(0, 0, 0, 255);
362             backgroundThumbnail.Resize(480, 270);
363             backgroundThumbnail.Move(App.MainWindow.Geometry.Width - 96 - backgroundThumbnail.Geometry.Width, App.MainWindow.Geometry.Height - 96 - backgroundThumbnail.Geometry.Height);
364             backgroundThumbnail.Show();
365
366             thumbnailImg = new ElmSharp.Image(App.MainWindow);
367             thumbnailImg.Load((ThumbnailPath != null) ? ThumbnailPath : (CurrentImage.Source as FileImageSource).File);
368             thumbnailImg.Orientation = imgOrientation;
369             thumbnailImg.Resize(backgroundThumbnail.Geometry.Width, backgroundThumbnail.Geometry.Height);
370             thumbnailImg.Move(backgroundThumbnail.Geometry.X, backgroundThumbnail.Geometry.Y);
371             thumbnailImg.Show();
372
373             thumbnailDim = new ElmSharp.Image(App.MainWindow);
374             thumbnailDim.Load(Path.Combine(Program.Current.DirectoryInfo.Resource, "img_zoom_minimap_dimmed.png"));
375             thumbnailDim.Resize(thumbnailImg.Geometry.Width, thumbnailImg.Geometry.Height);
376             thumbnailDim.Geometry = thumbnailImg.Geometry;
377             thumbnailDim.Moved += (s, e) =>
378             {
379                 thumbnailDim.Geometry = ((ElmSharp.Rectangle)s).Geometry;
380             };
381             thumbnailDim.Show();
382
383             clippingImg = new ElmSharp.Image(App.MainWindow);
384             clippingImg.Load((ThumbnailPath != null) ? ThumbnailPath : (CurrentImage.Source as FileImageSource).File);
385             clippingImg.Orientation = imgOrientation;
386             clippingImg.Resize(thumbnailImg.Geometry.Width, thumbnailImg.Geometry.Height);
387             clippingImg.Geometry = thumbnailImg.Geometry;
388             clippingImg.Moved += (s, e) =>
389             {
390                 clippingImg.Geometry = ((ElmSharp.Rectangle)s).Geometry;
391             };
392             clippingImg.Show();
393
394             clipper = new ElmSharp.Rectangle(App.MainWindow);
395             clipper.Color = new ElmSharp.Color(255, 255, 255, 255);
396             clipper.Resize((int)(backgroundThumbnail.Geometry.Width / zoomRatio), (int)(backgroundThumbnail.Geometry.Height / zoomRatio));
397             clipper.Move(thumbnailDim.Geometry.X + (thumbnailDim.Geometry.Width / 2) - (clipper.Geometry.Width / 2), thumbnailDim.Geometry.Y + (thumbnailDim.Geometry.Height / 2) - (clipper.Geometry.Height / 2));
398             clippingImg.SetClip(clipper);
399             clipper.Show();
400
401             clipperBorder = new ElmSharp.Image(App.MainWindow)
402             {
403                 IsFixedAspect = false,
404             };
405             clipperBorder.Load(Path.Combine(Program.Current.DirectoryInfo.Resource, "img_zoom_minimap_line.9.png"));
406             clipperBorder.Resize(clipper.Geometry.Width, clipper.Geometry.Height);
407             clipperBorder.Geometry = clipper.Geometry;
408             clipperBorder.SetBorder(clipperBorder.Geometry.Width / 2, clipperBorder.Geometry.Width / 2, clipperBorder.Geometry.Height / 2, clipperBorder.Geometry.Height / 2);
409             clipperBorder.Show();
410
411             clipper.Moved += (s, e) =>
412             {
413                 clipperBorder.Move(clipper.Geometry.X, clipper.Geometry.Y);
414             };
415
416             object[] buttons = new object[4] { UpButton, DownButton, LeftButton, RightButton };
417             foreach (object button in buttons)
418             {
419                 (button as CustomImageButton).OnButtonDown += (s, e) =>
420                 {
421                     DbgPort.D("'" + e + "' is pressed");
422
423                     MoveCurrentImage(e as string);
424                 };
425             }
426         }
427
428         protected override void LayoutChildren(double x, double y, double width, double height)
429         {
430             base.LayoutChildren(x, y, width, height);
431             GradientDim.HeightRequest = height;
432             GradientDim.WidthRequest = width;
433         }
434
435         public override void RunHideAnimation()
436         {
437         }
438
439         public override void RunShowAnimation()
440         {
441         }
442     }
443 }