diff --git a/TINK/TINK/View/Map/MapPage.xaml.cs b/TINK/TINK/View/Map/MapPage.xaml.cs index 02a8d14..a2737a9 100644 --- a/TINK/TINK/View/Map/MapPage.xaml.cs +++ b/TINK/TINK/View/Map/MapPage.xaml.cs @@ -22,7 +22,7 @@ namespace TINK.View.Map private MapPageViewModel MapPageViewModel { get; set; } /// Initialization status to ensure initialization logic is not called multiple times. - private bool viewInitialized = false; + private bool isInitializationStarted = false; /// /// Constructs map page instance. @@ -136,12 +136,13 @@ namespace TINK.View.Map protected async override void OnAppearing() { // Don't repeat the initialization if it has been completed already. - if (viewInitialized) return; + if (isInitializationStarted) return; + isInitializationStarted = true; // Pass reference to member Navigation to show bikes at station x dialog. try { - MapPageViewModel = createMapPageViewModel(); + MapPageViewModel = CreateMapPageViewModel(); } catch (Exception exception) { @@ -166,7 +167,7 @@ namespace TINK.View.Map try { - applyCustomiOSStyling(); + ApplyCustomiOSStyling(); } catch (Exception exception) { @@ -188,7 +189,7 @@ namespace TINK.View.Map try { // Pre move and scanle maps to avoid initial display of map in Rome. - premoveAndScaleMap(); + PremoveAndScaleMap(); } catch(Exception exception) { @@ -206,14 +207,13 @@ namespace TINK.View.Map Log.ForContext().Error("Invoking OnAppearing on map page view model failed. {Exception}", exception); return; } - viewInitialized = true; } /// /// Premoves the Map to a certain location. /// - private void premoveAndScaleMap() + private void PremoveAndScaleMap() { Log.ForContext().Verbose("Moving and scaling map."); MapPageViewModel.MoveAndScale( @@ -226,7 +226,7 @@ namespace TINK.View.Map /// /// Creates the Map Page's view model. /// - private MapPageViewModel createMapPageViewModel() + private MapPageViewModel CreateMapPageViewModel() { Log.ForContext().Verbose("Constructing map page view model."); return new MapPageViewModel( @@ -243,7 +243,7 @@ namespace TINK.View.Map /// /// Applies iOS specific styling to branded Buttons. /// - private void applyCustomiOSStyling() + private void ApplyCustomiOSStyling() { if (Device.RuntimePlatform == Device.iOS) { @@ -267,7 +267,6 @@ namespace TINK.View.Map // View model might be null. await MapPageViewModel?.OnDisappearing(); } - viewInitialized = false; base.OnDisappearing(); } } diff --git a/TINKLib/ViewModel/Map/MapPageViewModel.cs b/TINKLib/ViewModel/Map/MapPageViewModel.cs index 2d55ccc..f0923e2 100644 --- a/TINKLib/ViewModel/Map/MapPageViewModel.cs +++ b/TINKLib/ViewModel/Map/MapPageViewModel.cs @@ -316,97 +316,16 @@ namespace TINK.ViewModel.Map ActionText = AppResources.ActivityTextRequestingLocationPermissions; - // Check location permission - var status = await PermissionsService.CheckStatusAsync(); - if (TinkApp.CenterMapToCurrentLocation - && !GeolocationService.IsSimulation - && status != Status.Granted) - { - status = await PermissionsService.RequestAsync(); - - if (status != Status.Granted) - { - var dialogResult = await ViewService.DisplayAlert( - AppResources.MessageTitleHint, - AppResources.MessageCenterMapLocationPermissionOpenDialog, - AppResources.MessageAnswerYes, - AppResources.MessageAnswerNo); - - if (dialogResult) - { - // User decided to give access to locations permissions. - PermissionsService.OpenAppSettings(); - ActionText = ""; - IsRunning = false; - IsMapPageEnabled = true; - return; - } - } - } - ActionText = AppResources.ActivityTextMapLoadingStationsAndBikes; IsConnected = TinkApp.GetIsConnected(); - var resultStationsAndBikes = await TinkApp.GetConnector(IsConnected).Query.GetBikesAndStationsAsync(); + + Result resultStationsAndBikes = await TinkApp.GetConnector(IsConnected).Query.GetBikesAndStationsAsync(); TinkApp.Stations = resultStationsAndBikes.Response.StationsAll; - if (Pins.Count > 0 && Pins.Count != resultStationsAndBikes.Response.StationsAll.Count) - { - // Either - // - user logged in/ logged out which might lead to more/ less stations beeing available - // - new stations were added/ existing ones remove - Pins.Clear(); - } - - // Check if there are alreay any pins to the map - // i.e detecte first call of member OnAppearing after construction - if (Pins.Count <= 0) - { - Log.ForContext().Debug($"{(ActiveFilterMap.GetGroup().Any() ? $"Active map filter is {string.Join(",", ActiveFilterMap.GetGroup())}." : "Map filter is off.")}"); - - // Map was not yet initialized. - // Get stations from Copri - Log.ForContext().Verbose("No pins detected on page."); - if (resultStationsAndBikes.Response.StationsAll.CopriVersion < CopriCallsStatic.UnsupportedVersionLower) - { - await ViewService.DisplayAlert( - AppResources.MessageWaring, - string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), - AppResources.MessageAnswerOk); - - Log.ForContext().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}."); - } - - if (resultStationsAndBikes.Response.StationsAll.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper) - { - await ViewService.DisplayAlert( - AppResources.MessageWaring, - string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), - AppResources.MessageAnswerOk); - - Log.ForContext().Error($"Outdated version of app detected. Version expected is {resultStationsAndBikes.Response.StationsAll.CopriVersion}."); - } - - // Set pins to their positions on map. - InitializePins(resultStationsAndBikes.Response.StationsAll); - - Log.ForContext().Verbose("Update of pins done."); - } - - - if (resultStationsAndBikes.Exception?.GetType() == typeof(AuthcookieNotDefinedException)) - { - Log.ForContext().Error("Map page is shown (probable for the first time after startup of app) and COPRI auth cookie is not defined. {@l_oException}", resultStationsAndBikes.Exception); - - // COPRI reports an auth cookie error. - await ViewService.DisplayAlert( - AppResources.MessageWaring, - AppResources.MessageMapPageErrorAuthcookieUndefined, - AppResources.MessageAnswerOk); - - await TinkApp.GetConnector(IsConnected).Command.DoLogout(); - TinkApp.ActiveUser.Logout(); - } + await SetStationsOnMap(resultStationsAndBikes.Response.StationsAll); + await HandleAuthCookieNotDefinedException(resultStationsAndBikes.Exception); + // Update pin colors. Log.ForContext().Verbose("Starting update pins color..."); @@ -418,15 +337,13 @@ namespace TINK.ViewModel.Map // Update pins color form count of bikes located at station. UpdatePinsColor(colors); + Log.ForContext().Verbose("Update pins color done."); + + // Move and scale before getting stations and bikes which takes some time. ActionText = AppResources.ActivityTextCenterMap; - - moveMapToCurrentPositionOfUser(); - m_oViewUpdateManager = CreateUpdateTask(); - Log.ForContext().Verbose("Update pins color done."); - try { // Update bikes at station or my bikes depending on context. @@ -441,6 +358,8 @@ namespace TINK.ViewModel.Map ActionText = ""; IsRunning = false; IsMapPageEnabled = true; + + await MoveMapToCurrentPositionOfUser(); } catch (Exception l_oException) { @@ -457,28 +376,137 @@ namespace TINK.ViewModel.Map } } + + /// + /// Invoked when the auth cookie is not defined. + /// + private async Task HandleAuthCookieNotDefinedException(Exception exception) + { + if (exception?.GetType() == typeof(AuthcookieNotDefinedException)) + { + Log.ForContext().Error("Map page is shown (probable for the first time after startup of app) and COPRI auth cookie is not defined. {@l_oException}", exception); + + // COPRI reports an auth cookie error. + await ViewService.DisplayAlert( + AppResources.MessageWaring, + AppResources.MessageMapPageErrorAuthcookieUndefined, + AppResources.MessageAnswerOk); + + await TinkApp.GetConnector(IsConnected).Command.DoLogout(); + TinkApp.ActiveUser.Logout(); + } + } + + /// + /// Sets the available stations on the map. + /// + private async Task SetStationsOnMap(StationDictionary stations) + { + if (Pins.Count > 0 && Pins.Count != stations.Count) + { + // Either + // - user logged in/ logged out which might lead to more/ less stations beeing available + // - new stations were added/ existing ones remove + Pins.Clear(); + } + + // Check if there are alreay any pins to the map + // i.e detecte first call of member OnAppearing after construction + if (Pins.Count <= 0) + { + Log.ForContext().Debug($"{(ActiveFilterMap.GetGroup().Any() ? $"Active map filter is {string.Join(",", ActiveFilterMap.GetGroup())}." : "Map filter is off.")}"); + + // Map was not yet initialized. + // Get stations from Copri + Log.ForContext().Verbose("No pins detected on page."); + if (stations.CopriVersion < CopriCallsStatic.UnsupportedVersionLower) + { + await ViewService.DisplayAlert( + AppResources.MessageWaring, + string.Format(AppResources.MessageCopriVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + + Log.ForContext().Error($"Outdated version of app detected. Version expected is {stations.CopriVersion}."); + } + + if (stations.CopriVersion >= CopriCallsStatic.UnsupportedVersionUpper) + { + await ViewService.DisplayAlert( + AppResources.MessageWaring, + string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)), + AppResources.MessageAnswerOk); + + Log.ForContext().Error($"Outdated version of app detected. Version expected is {stations.CopriVersion}."); + } + + // Set pins to their positions on map. + InitializePins(stations); + + Log.ForContext().Verbose("Update of pins done."); + } + + } + /// /// Moves the map to the current position of the user. /// If location permission hasn't been granted, the position is not adjusted. /// - private async void moveMapToCurrentPositionOfUser() + private async Task MoveMapToCurrentPositionOfUser() { + + Status status = await RequestLocationPermission(); + if (status == Status.Granted) + { + Location currentLocation = null; + try + { + currentLocation = TinkApp.CenterMapToCurrentLocation + ? await GeolocationService.GetAsync() + : null; + } + catch (Exception ex) + { + Log.ForContext().Error("Getting location failed. {Exception}", ex); + } + + MoveAndScale(m_oMoveToRegionDelegate, TinkApp.Uris.ActiveUri, ActiveFilterMap, currentLocation); + }; + } + + /// + /// Requests the location permission from the user. + /// If the user declines, a dialog prompot is shown, telling the user to toggle the permission in the device settings. + /// + /// The permission status. + private async Task RequestLocationPermission() + { + // Check location permission var status = await PermissionsService.CheckStatusAsync(); - if (status != Status.Granted) return; - Location currentLocation = null; - try + if (TinkApp.CenterMapToCurrentLocation + && !GeolocationService.IsSimulation + && status != Status.Granted) { - currentLocation = TinkApp.CenterMapToCurrentLocation - ? await GeolocationService.GetAsync() - : null; - } - catch (Exception ex) - { - Log.ForContext().Error("Getting location failed. {Exception}", ex); - } + status = await PermissionsService.RequestAsync(); - MoveAndScale(m_oMoveToRegionDelegate, TinkApp.Uris.ActiveUri, ActiveFilterMap, currentLocation); + if (status != Status.Granted) + { + var dialogResult = await ViewService.DisplayAlert( + AppResources.MessageTitleHint, + AppResources.MessageCenterMapLocationPermissionOpenDialog, + AppResources.MessageAnswerYes, + AppResources.MessageAnswerNo); + if (dialogResult) + { + // User decided to give access to locations permissions. + PermissionsService.OpenAppSettings(); + ActionText = ""; + IsRunning = false; + IsMapPageEnabled = true; + } + } + } + return status; } /// Moves map and scales visible region depending on active filter.