Version 3.0.255

This commit is contained in:
Oliver Hauff 2021-11-07 19:42:59 +01:00
parent db9c288584
commit 5a26bf273b
1495 changed files with 159465 additions and 5060 deletions

View file

@ -110,7 +110,7 @@ namespace TINK.Model.Settings
}).ToDictionary(key => key.Key, value => value.Value);
}
/// <summary> Sets the uri of the active copri host. </summary>
/// <param name="p_oSettingsJSON">Dictionary holding parameters from JSON.</param>
/// <param name="settingsJSON">Dictionary holding parameters from JSON.</param>
public static Dictionary<string, string> SetCopriHostUri(this IDictionary<string, string> p_oTargetDictionary, string p_strNextActiveUriText)
{
if (p_oTargetDictionary == null)
@ -123,11 +123,11 @@ namespace TINK.Model.Settings
}
/// <summary> Gets the timeout to apply when connecting to bluetooth lock.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get information from.</param>
/// <param name="settingsJSON">Dictionary to get information from.</param>
/// <returns>Connect timeout value.</returns>
public static TimeSpan? GetConnectTimeout(Dictionary<string, string> p_oSettingsJSON)
public static TimeSpan? GetConnectTimeout(Dictionary<string, string> settingsJSON)
{
if (!p_oSettingsJSON.TryGetValue(CONNECTTIMEOUT, out string connectTimeout)
if (!settingsJSON.TryGetValue(CONNECTTIMEOUT, out string connectTimeout)
|| string.IsNullOrEmpty(connectTimeout))
{
// File holds no entry.
@ -159,7 +159,7 @@ namespace TINK.Model.Settings
}
/// <summary> Sets the version of the app. </summary>
/// <param name="p_oSettingsJSON">Dictionary holding parameters from JSON.</param>
/// <param name="settingsJSON">Dictionary holding parameters from JSON.</param>
public static Dictionary<string, string> SetAppVersion(this IDictionary<string, string> p_oTargetDictionary, Version p_strAppVersion)
{
if (p_oTargetDictionary == null)
@ -172,12 +172,12 @@ namespace TINK.Model.Settings
}
/// <summary> Gets the app versions.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get logging level from.</param>
/// <param name="settingsJSON">Dictionary to get logging level from.</param>
/// <returns>Logging level</returns>
public static Version GetAppVersion(this IDictionary<string, string> p_oSettingsJSON)
public static Version GetAppVersion(this IDictionary<string, string> settingsJSON)
{
// Get the version of the app which wrote the settings file.
if (!p_oSettingsJSON.TryGetValue(APPVERIONKEY, out string l_oAppVersion)
if (!settingsJSON.TryGetValue(APPVERIONKEY, out string l_oAppVersion)
|| string.IsNullOrEmpty(l_oAppVersion))
{
// File holds no entry.
@ -191,7 +191,7 @@ namespace TINK.Model.Settings
/// <summary> Sets whether polling is on or off and the periode if polling is on. </summary>
/// <param name="p_oSettingsJSON">Dictionary to write entries to.</param>
/// <param name="settingsJSON">Dictionary to write entries to.</param>
public static Dictionary<string, string> SetPollingParameters(this IDictionary<string, string> p_oTargetDictionary, PollingParameters p_oPollingParameter)
{
if (p_oTargetDictionary == null)
@ -205,13 +205,13 @@ namespace TINK.Model.Settings
}
/// <summary> Get whether polling is on or off and the periode if polling is on. </summary>
/// <param name="p_oSettingsJSON">Dictionary holding parameters from JSON.</param>
/// <param name="settingsJSON">Dictionary holding parameters from JSON.</param>
/// <returns>Polling parameters.</returns>
public static PollingParameters GetPollingParameters(this IDictionary<string, string> p_oSettingsJSON)
public static PollingParameters GetPollingParameters(this IDictionary<string, string> settingsJSON)
{
// Check if dictionary contains entry for periode.
if (p_oSettingsJSON.TryGetValue($"{typeof(PollingParameters).Name}_{typeof(TimeSpan).Name}", out string l_strPeriode)
&& p_oSettingsJSON.TryGetValue($"{typeof(PollingParameters).Name}_{typeof(bool).Name}", out string l_strIsActive)
if (settingsJSON.TryGetValue($"{typeof(PollingParameters).Name}_{typeof(TimeSpan).Name}", out string l_strPeriode)
&& settingsJSON.TryGetValue($"{typeof(PollingParameters).Name}_{typeof(bool).Name}", out string l_strIsActive)
&& !string.IsNullOrEmpty(l_strPeriode)
&& !string.IsNullOrEmpty(l_strIsActive))
{
@ -260,13 +260,13 @@ namespace TINK.Model.Settings
}
/// <summary> Gets the logging level.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get logging level from.</param>
/// <param name="settingsJSON">Dictionary to get logging level from.</param>
/// <returns>Logging level.</returns>
public static LogEventLevel? GetMinimumLoggingLevel(
Dictionary<string, string> p_oSettingsJSON)
Dictionary<string, string> settingsJSON)
{
// Get logging level.
if (!p_oSettingsJSON.TryGetValue(MINLOGGINGLEVELKEY, out string l_strLevel)
if (!settingsJSON.TryGetValue(MINLOGGINGLEVELKEY, out string l_strLevel)
|| string.IsNullOrEmpty(l_strLevel))
{
// File holds no entry.
@ -281,13 +281,13 @@ namespace TINK.Model.Settings
public static bool? GetIsReportLevelVerbose(Dictionary<string, string> settingsJSON) => GetNullableEntry<bool>(ISREPORTLEVELVERBOSEKEY, settingsJSON);
/// <summary> Sets a value indicating whether report level is verbose or not.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get value from.</param>
/// <param name="settingsJSON">Dictionary to get value from.</param>
public static Dictionary<string, string> SetIsReportLevelVerbose(this IDictionary<string, string> targetDictionary, bool isReportLevelVerbose)
=> SetEntry(isReportLevelVerbose, ISREPORTLEVELVERBOSEKEY, targetDictionary);
/// <summary> Sets the logging level.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get logging level from.</param>
/// <param name="settingsJSON">Dictionary to get logging level from.</param>
public static Dictionary<string, string> SetMinimumLoggingLevel(this IDictionary<string, string> p_oTargetDictionary, LogEventLevel p_oLevel)
{
// Set logging level.
@ -301,12 +301,12 @@ namespace TINK.Model.Settings
}
/// <summary> Gets the version of app when whats new was shown.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get logging level from.</param>
/// <param name="settingsJSON">Dictionary to get logging level from.</param>
/// <returns>Version of the app.</returns>
public static Version GetWhatsNew(Dictionary<string, string> p_oSettingsJSON)
public static Version GetWhatsNew(Dictionary<string, string> settingsJSON)
{
// Get logging level.
if (!p_oSettingsJSON.TryGetValue(SHOWWHATSNEWKEY, out string l_strWhatsNewVersion)
if (!settingsJSON.TryGetValue(SHOWWHATSNEWKEY, out string l_strWhatsNewVersion)
|| string.IsNullOrEmpty(l_strWhatsNewVersion))
{
// File holds no entry.
@ -317,7 +317,7 @@ namespace TINK.Model.Settings
}
/// <summary> Sets the version of app when whats new was shown.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get information from.</param>
/// <param name="settingsJSON">Dictionary to get information from.</param>
public static Dictionary<string, string> SetWhatsNew(this IDictionary<string, string> p_oTargetDictionary, Version p_oAppVersion)
{
// Set logging level.
@ -331,11 +331,11 @@ namespace TINK.Model.Settings
}
/// <summary> Gets the expires after value.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get expries after value from.</param>
/// <param name="settingsJSON">Dictionary to get expries after value from.</param>
/// <returns>Expires after value.</returns>
public static TimeSpan? GetExpiresAfter(Dictionary<string, string> p_oSettingsJSON)
public static TimeSpan? GetExpiresAfter(Dictionary<string, string> settingsJSON)
{
if (!p_oSettingsJSON.TryGetValue(EXPIRESAFTER, out string expiresAfter)
if (!settingsJSON.TryGetValue(EXPIRESAFTER, out string expiresAfter)
|| string.IsNullOrEmpty(expiresAfter))
{
// File holds no entry.
@ -346,7 +346,7 @@ namespace TINK.Model.Settings
}
/// <summary> Sets the the expiration time.</summary>
/// <param name="p_oSettingsJSON">Dictionary to write information to.</param>
/// <param name="settingsJSON">Dictionary to write information to.</param>
public static Dictionary<string, string> SetExpiresAfter(this IDictionary<string, string> p_oTargetDictionary, TimeSpan expiresAfter)
{
if (p_oTargetDictionary == null)
@ -429,7 +429,7 @@ namespace TINK.Model.Settings
public static bool? GetCenterMapToCurrentLocation(Dictionary<string, string> settingsJSON) => GetNullableEntry<bool>(CENTERMAPTOCURRENTLOCATION, settingsJSON);
/// <summary> Sets a value indicating whether to center the map to location or not.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get value from.</param>
/// <param name="settingsJSON">Dictionary to get value from.</param>
public static Dictionary<string, string> SetCenterMapToCurrentLocation(this IDictionary<string, string> targetDictionary, bool centerMapToCurrentLocation)
=> SetEntry(centerMapToCurrentLocation, CENTERMAPTOCURRENTLOCATION, targetDictionary);
@ -446,7 +446,7 @@ namespace TINK.Model.Settings
public static bool? GetIsSiteCachingOn(Dictionary<string, string> settingsJSON) => GetNullableEntry<bool>(ISSITECACHINGON, settingsJSON);
/// <summary> Sets whether to store logging data on SD card or not.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get value from.</param>
/// <param name="settingsJSON">Dictionary to get value from.</param>
public static Dictionary<string, string> SetLogToExternalFolder(this IDictionary<string, string> targetDictionary, bool useSdCard) => SetEntry(useSdCard, LOGTOEXTERNALFOLDER, targetDictionary);
/// <summary> Sets active theme.</summary>
@ -454,7 +454,7 @@ namespace TINK.Model.Settings
public static Dictionary<string, string> SetActiveTheme(this IDictionary<string, string> targetDictionary, string theme) => SetEntry(theme, THEMEKEY, targetDictionary);
/// <summary> Sets whether site caching is on or off.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get value from.</param>
/// <param name="settingsJSON">Dictionary to get value from.</param>
public static Dictionary<string, string> SetIsSiteCachingOn(this IDictionary<string, string> targetDictionary, bool useSdCard) => SetEntry(useSdCard, ISSITECACHINGON, targetDictionary);
/// <summary> Gets the map page filter. </summary>

View file

@ -16,6 +16,9 @@ namespace TINK.Model.Settings
public const LogEventLevel DEFAULTLOGGINLEVEL = LogEventLevel.Error;
public const bool DEFAULTREPOTLEVEL = false;
/// <summary> Gets the type of the default geolocation service. </summary>
public static Type DefaultLocationService => typeof(GeolocationService);
// Default value of the expires after entry. Controls the expiration time of the cache values.
private TimeSpan DEFAULTEXPIRESAFTER = TimeSpan.FromSeconds(1);
@ -56,7 +59,7 @@ namespace TINK.Model.Settings
ExpiresAfter = expiresAfter ?? DEFAULTEXPIRESAFTER;
ActiveLockService = activeLockService ?? LocksServicesContainerMutable.DefaultLocksservice;
ConnectTimeout = connectTimeout ?? new TimeSpan(0, 0, TimeOutProvider.DEFAULT_BLUETOOTHCONNECT_TIMEOUTSECONDS); // Try one sec. to connect.
ActiveGeolocationService = activeGeolocationService ?? typeof(LastKnownGeolocationService).Name;
ActiveGeolocationService = activeGeolocationService ?? DefaultLocationService.Name;
CenterMapToCurrentLocation = centerMapToCurrentLocation ?? GetCenterMapToCurrentLocation(activeUri);
LogToExternalFolder = logToExternalFolder ?? false;
IsSiteCachingOn = isSiteCachingOn ?? true;

View file

@ -15,6 +15,9 @@ namespace TINK.Model.State
/// <param name="p_eValue">State value.</param>
protected BaseState(InUseStateEnum p_eValue) {}
/// <summary>
/// Holds the state value.
/// </summary>
public abstract InUseStateEnum Value { get; }
}
}

View file

@ -28,7 +28,7 @@ namespace TINK.Model.State
/// </summary>
public class StateInfo : IStateInfo
{
// Holds the current disposable state value
// Holds the current disposable state value.
private readonly BaseState m_oInUseState;
/// <summary>

View file

@ -6,14 +6,14 @@ namespace TINK.Model.Station
{
private const double PRECISSION_LATITUDE_LONGITUDE = 0.000000000000001;
public Position()
public Position()
{
}
public Position(double p_dLatitude, double p_dLongitude)
public Position(double latitude, double longitude)
{
Latitude = p_dLatitude;
Longitude = p_dLongitude;
Latitude = latitude;
Longitude = longitude;
}
public double Latitude { get; private set; }
@ -35,7 +35,6 @@ namespace TINK.Model.Station
return Math.Abs(Latitude - l_oTarget.Latitude) < PRECISSION_LATITUDE_LONGITUDE
&& Math.Abs(Longitude - l_oTarget.Longitude) < PRECISSION_LATITUDE_LONGITUDE;
}
public override int GetHashCode()

View file

@ -193,7 +193,11 @@ namespace TINK.Model
LocksServices.SetTimeOut(settings.ConnectTimeout);
Themes = new ServicesContainerMutable<object>(
new HashSet<object> { new Themes.Konrad() , new Themes.ShareeBike() },
new HashSet<object> {
new Themes.Konrad(),
new Themes.ShareeBike(),
new Themes.LastenradBayern()
},
settings.ActiveTheme);
GeolocationServices = geolocationServicesContainer
@ -204,11 +208,6 @@ namespace TINK.Model
{
FilterGroupSetting = settings.GroupFilterSettings;
GroupFilterMapPage = settings.GroupFilterMapPage;
//} else if (settings.ActiveUri == new Uri(CopriServerUriList.SHAREE_LIVE) ||
// settings.ActiveUri == new Uri(CopriServerUriList.SHAREE_DEVEL))
//{
// FilterGroupSetting = new GroupFilterSettings(new Dictionary<string, FilterState> { { "300001", FilterState.On }, { "300029", FilterState.On } });
// FilterGroupMapPage = new GroupFilterMapPage();
} else
{
FilterGroupSetting = new GroupFilterSettings();
@ -269,7 +268,11 @@ namespace TINK.Model
WhatsNew = new WhatsNew(AppVersion, lastVersion, whatsNewShownInVersion);
if (Themes.Active.GetType().FullName == typeof(Themes.ShareeBike).FullName)
{
// Nothing to do.
// Theme to activate is default theme.
return;
}
// Set active app theme
ICollection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
@ -284,6 +287,10 @@ namespace TINK.Model
if (Themes.Active.GetType().FullName == typeof(Themes.Konrad).FullName)
{
mergedDictionaries.Add(new Themes.Konrad());
}
else if (Themes.Active.GetType().FullName == typeof(Themes.LastenradBayern).FullName)
{
mergedDictionaries.Add(new Themes.LastenradBayern());
}
else
{

View file

@ -50,24 +50,24 @@ namespace TINK.Model.User.Account
public class Account : IAccount
{
/// <summary> Constructs an account object.</summary>
/// <param name="mail">Mail addresss.</param>
/// <param name="mail">Mail address of the account holder.</param>
/// <param name="password">Password.</param>
/// <param name="sessionCookie">Session cookie from copri.</param>
/// <param name="group">Group holdig info about Group (TINK, Konrad, ...)</param>
/// <param name="bikeGroup">Group holdig info about Group (TINK, Konrad, ...)</param>
/// <param name="p_iDebugLevel">Flag which controls display of debug settings.</param>
public Account(
string mail,
string password,
string sessionCookie,
IEnumerable<string> group,
IEnumerable<string> bikeGroup,
Permissions debugLevel = Permissions.None)
{
Mail = mail;
Pwd = password;
SessionCookie = sessionCookie;
DebugLevel = debugLevel;
Group = group != null
? new HashSet<string>(group).ToList()
Group = bikeGroup != null
? new HashSet<string>(bikeGroup).ToList()
: throw new ArgumentException("Can not instantiate account object. Reference to group list must not be empty.");
}
@ -75,10 +75,10 @@ namespace TINK.Model.User.Account
{
}
/// <summary>Mail address.</summary>
/// <summary>Mail address of the account holder.</summary>
public string Mail { get; }
/// <summary>Password of the account.</summary>
/// <summary>Password of to authenticate.</summary>
public string Pwd { get; }
/// <summary>Session cookie used to sign in to copri.</summary>

View file

@ -429,6 +429,35 @@ namespace TINK.Model
{
new Version(3, 0, 244),
AppResources.ChangeLog3_0_231 // Minor improvements.
},
{
new Version(3, 0, 245),
AppResources.ChangeLog3_0_231 // Minor improvements.
},
{
new Version(3, 0, 246),
AppResources.ChangeLog3_0_231 // Minor improvements.
},
{
new Version(3, 0, 247),
AppResources.ChangeLog3_0_231 // Minor improvements.
},
{
new Version(3, 0, 248),
AppResources.ChangeLog3_0_231 // Minor improvements.
},
{
new Version(3, 0, 249),
AppResources.ChangeLog3_0_249 // Third-party components updated.
},
{
new Version(3, 0, 250),
AppResources.ChangeLog3_0_250 // Third-party components updated.
},
{
new Version(3, 0, 254),
// Same info as for version 3.0.251 and 3.0.252
AppResources.ChangeLog3_0_231 // Minor improvements.
}
};

View file

@ -851,6 +851,26 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Third-party components updated..
/// </summary>
public static string ChangeLog3_0_249 {
get {
return ResourceManager.GetString("ChangeLog3_0_249", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Station id displayed.
///Center map to current position issue fixed.
///Minor improvements..
/// </summary>
public static string ChangeLog3_0_250 {
get {
return ResourceManager.GetString("ChangeLog3_0_250", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock of rented bike can not be found..
/// </summary>
@ -1174,6 +1194,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Station id: {0}.
/// </summary>
public static string MarkingBikesAtStationStationId {
get {
return ResourceManager.GetString("MarkingBikesAtStationStationId", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Operator: {0}..
/// </summary>

View file

@ -699,4 +699,15 @@ Kleinere Verbesserungen.</value>
<data name="ChangeLog3_0_244" xml:space="preserve">
<value>Abschließen von Rad und Radrückgabe beschleunigt.</value>
</data>
<data name="ChangeLog3_0_249" xml:space="preserve">
<value>Komponenten von Fremdanbietern aktualisiert.</value>
</data>
<data name="MarkingBikesAtStationStationId" xml:space="preserve">
<value>Stationskennung: {0}</value>
</data>
<data name="ChangeLog3_0_250" xml:space="preserve">
<value>Anzeige Stationskennung.
Kartenzentrierfehler behoben.
Kleine Verbesserungen.</value>
</data>
</root>

View file

@ -794,4 +794,15 @@ Minor fixes.</value>
<data name="ErrorReturnBikeNoWebTitle" xml:space="preserve">
<value>Connection error when returning the bike!</value>
</data>
<data name="ChangeLog3_0_249" xml:space="preserve">
<value>Third-party components updated.</value>
</data>
<data name="MarkingBikesAtStationStationId" xml:space="preserve">
<value>Station id: {0}</value>
</data>
<data name="ChangeLog3_0_250" xml:space="preserve">
<value>Station id displayed.
Center map to current position issue fixed.
Minor improvements.</value>
</data>
</root>

View file

@ -936,6 +936,22 @@ Kleinere Verbesserungen.</target>
<source>Closing lock and returning bike speeded up.</source>
<target state="translated">Abschließen von Rad und Radrückgabe beschleunigt.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_249" translate="yes" xml:space="preserve">
<source>Third-party components updated.</source>
<target state="translated">Komponenten von Fremdanbietern aktualisiert.</target>
</trans-unit>
<trans-unit id="MarkingBikesAtStationStationId" translate="yes" xml:space="preserve">
<source>Station id: {0}</source>
<target state="translated">Stationskennung: {0}</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_250" translate="yes" xml:space="preserve">
<source>Station id displayed.
Center map to current position issue fixed.
Minor improvements.</source>
<target state="translated">Anzeige Stationskennung.
Kartenzentrierfehler behoben.
Kleine Verbesserungen.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -38,25 +38,25 @@ namespace TINK.Model.Services.Geolocation
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
Log.ForContext<LastKnownGeolocationService>().Error("Retrieving Geolocation not supported on device. {Exception}", fnsEx);
Log.ForContext<GeolocationService>().Error("Retrieving Geolocation not supported on device. {Exception}", fnsEx);
throw new Exception("Abfrage Standort nicht möglich auf Gerät.", fnsEx);
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
Log.ForContext<LastKnownGeolocationService>().Error("Retrieving Geolocation not enabled on device. {Exception}", fneEx);
Log.ForContext<GeolocationService>().Error("Retrieving Geolocation not enabled on device. {Exception}", fneEx);
throw new Exception("Abfrage Standort nicht aktiviert auf Gerät.", fneEx);
}
catch (PermissionException pEx)
{
// Handle permission exception
Log.ForContext<LastKnownGeolocationService>().Error("Retrieving Geolocation not permitted on device. {Exception}", pEx);
Log.ForContext<GeolocationService>().Error("Retrieving Geolocation not permitted on device. {Exception}", pEx);
throw new Exception("Berechtiung für Abfrage Standort nicht erteilt für App.", pEx);
}
catch (Exception ex)
{
// Unable to get location
Log.ForContext<LastKnownGeolocationService>().Error("Retrieving Geolocation failed. {Exception}", ex);
Log.ForContext<GeolocationService>().Error("Retrieving Geolocation failed. {Exception}", ex);
throw new Exception("Abfrage Standort fehlgeschlagen.", ex);
}
}

View file

@ -0,0 +1,80 @@
using System.Threading.Tasks;
using Xamarin.Essentials;
namespace TINK.Services.Permissions.Essentials
{
using XamPermission = Xamarin.Essentials.Permissions;
using XamPermissionStatus = PermissionStatus;
public class Permissions : ILocationPermission
{
/// <summary> Checks the permission status.</summary>
/// <returns>Current permission status.</returns>
public async Task<Status> CheckStatusAsync()
{
var status = await XamPermission.CheckStatusAsync<XamPermission.LocationWhenInUse>();
if (status == XamPermissionStatus.Granted)
{
return Status.Granted;
}
if (status == XamPermissionStatus.Denied
&& DeviceInfo.Platform == DevicePlatform.iOS)
{
// Prompt the user to turn on in settings
// On iOS once a permission has been denied it may not be requested again from the application
return Status.DeniedRequiresSettingsUI;
}
if (XamPermission.ShouldShowRationale<XamPermission.LocationWhenInUse>())
{
// Prompt the user with additional information as to why the permission is needed
return Status.Denied;
}
switch (status)
{
case XamPermissionStatus.Unknown:
case XamPermissionStatus.Denied: // Map Denied to unknown because "denied means user did not allow access to location".
return Status.Unknown;
default:
// Comprises:
// - XamPermissionStatus.Restricted:
// - XamPermissionStatus.Disabled
// Perission XamPermissionStatus.Granted is handled above.
return Status.DeniedRequiresSettingsUI;
}
}
/// <summary> Requests location permission.</summary>
/// <returns>Permission status after request.</returns>
public async Task<Status> RequestAsync()
{
switch (await XamPermission.RequestAsync<XamPermission.LocationWhenInUse>())
{
case XamPermissionStatus.Unknown:
return Status.Unknown;
case XamPermissionStatus.Denied:
return Status.Denied;
case XamPermissionStatus.Granted:
return Status.Granted;
default:
// Comprises:
// - XamPermissionStatus.Restricted:
// - XamPermissionStatus.Disabled
return Status.DeniedRequiresSettingsUI;
}
}
/// <summary> Opens app settings dialog.</summary>
public bool OpenAppSettings()
{
AppInfo.ShowSettingsUI();
return true;
}
}
}

View file

@ -0,0 +1,43 @@
using System.Threading.Tasks;
namespace TINK.Services.Permissions
{
public interface ILocationPermission
{
/// <summary> Checks the permission status.</summary>
/// <returns>Current permission status.</returns>
Task<Status> CheckStatusAsync();
/// <summary> Requests location permission.</summary>
/// <returns>Permission status after request.</returns>
Task<Status> RequestAsync();
bool OpenAppSettings();
}
/// <summary>
/// Holds the permission status.
/// </summary>
public enum Status
{
//
// Summary:
// The permission hasn't been granted or requested and is in an unknown state.
Unknown = 0,
//
// Summary:
// The user has denied the permission.
Denied = 1,
//
// Summary:
// The user has denied the permission and .
DeniedRequiresSettingsUI = 8,
//
// Summary:
// The user has granted permission.
Granted = 3,
}
}

View file

@ -0,0 +1,59 @@
using System.Threading.Tasks;
namespace TINK.Services.Permissions.Plugin
{
using global::Plugin.Permissions;
public class Permissions : ILocationPermission
{
/// <summary> Checks the permission status.</summary>
public async Task<Status> CheckStatusAsync()
{
switch (await CrossPermissions.Current.CheckPermissionStatusAsync<LocationPermission>())
{
case global::Plugin.Permissions.Abstractions.PermissionStatus.Denied:
return Status.Denied;
case global::Plugin.Permissions.Abstractions.PermissionStatus.Granted:
return Status.Granted;
case global::Plugin.Permissions.Abstractions.PermissionStatus.Unknown:
return Status.Unknown;
default:
// Comprises
// - PermissionStatus.Disabled and
// - PermissionStatus.Restricted.
return Status.DeniedRequiresSettingsUI;
}
}
/// <summary> Requests location permission.</summary>
/// <returns>Permission status after request.</returns>
public async Task<Status> RequestAsync()
{
switch (await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>())
{
case global::Plugin.Permissions.Abstractions.PermissionStatus.Denied:
return Status.Denied;
case global::Plugin.Permissions.Abstractions.PermissionStatus.Granted:
return Status.Granted;
case global::Plugin.Permissions.Abstractions.PermissionStatus.Unknown:
return Status.Unknown;
default:
// Comprises
// - PermissionStatus.Disabled and
// - PermissionStatus.Restricted.
return Status.DeniedRequiresSettingsUI;
}
}
/// <summary> Opens app settings dialog.</summary>
public bool OpenAppSettings()
=> CrossPermissions.Current.OpenAppSettings();
}
}

View file

@ -31,7 +31,7 @@
<PackageReference Include="Plugin.Permissions" Version="6.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
@ -42,14 +42,16 @@
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
<PackageReference Include="Xam.Plugin.Connectivity" Version="3.2.0" />
<PackageReference Include="Xam.Plugins.Messaging" Version="5.2.0" />
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2012" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" />
<PackageReference Include="Xamarin.Forms.GoogleMaps" Version="3.3.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="NETStandard.Library" Version="2.0.3" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\Permissions\Essentials\" />
<Folder Include="Services\Permissions\Plugin\" />
<Folder Include="ViewModel\Info\BikeInfo\" />
<Folder Include="ViewModel\FeesAndBikes\" />
</ItemGroup>

View file

@ -7,11 +7,13 @@ namespace TINK.Themes
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Konrad : ResourceDictionary, ITheme
{
public const string OPERATORINFO = "Mein konrad";
public Konrad ()
{
InitializeComponent ();
InitializeComponent();
}
public string OperatorInfo => "Konrad Konstanz";
public string OperatorInfo => OPERATORINFO;
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.Themes.LastenradBayern">
<!-- Pallete -->
<Color x:Key="primary-back-title-color">#009BDB</Color>
<!-- Pallete-end -->
<Style ApplyToDerivedTypes="true" TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{DynamicResource Key=primary-back-title-color}"/>
</Style>
</ResourceDictionary>

View file

@ -0,0 +1,19 @@
using TINK.View.Themes;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TINK.Themes
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LastenradBayern : ResourceDictionary, ITheme
{
public const string OPERATORINFO = "Lastenrad Bayern";
public LastenradBayern()
{
InitializeComponent();
}
public string OperatorInfo => OPERATORINFO;
}
}

View file

@ -7,11 +7,13 @@ namespace TINK.Themes
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ShareeBike : ResourceDictionary, ITheme
{
public const string OPERATORINFO = "sharee.bike";
public ShareeBike ()
{
InitializeComponent ();
}
public string OperatorInfo => string.Empty;
public string OperatorInfo => OPERATORINFO;
}
}

View file

@ -12,7 +12,7 @@ using TINK.Model.User;
using TINK.View;
using TINK.ViewModel.Bikes.Bike;
using TINK.ViewModel.Bikes.Bike.BC;
using Plugin.Permissions.Abstractions;
using TINK.Services.Permissions;
using Plugin.BLE.Abstractions.Contracts;
using TINK.MultilingualResources;
using TINK.Model.Device;
@ -91,7 +91,7 @@ namespace TINK.ViewModel.Bikes
/// <param name="viewService">Interface to actuate methodes on GUI.</param>
public BikesViewModel(
User user,
IPermissions permissions,
ILocationPermission permissions,
IBluetoothLE bluetoothLE,
string runtimPlatform,
Func<bool> isConnectedDelegate,
@ -231,7 +231,7 @@ namespace TINK.ViewModel.Bikes
/// <summary>
/// Service to manage permissions (location) of the app.
/// </summary>
protected IPermissions PermissionsService { get; private set; }
protected ILocationPermission PermissionsService { get; private set; }
/// <summary>
/// Service to manage bluetooth stack.

View file

@ -18,10 +18,9 @@ using TINK.Services.BluetoothLock;
using TINK.Model.Services.Geolocation;
using TINK.ViewModel.Bikes;
using TINK.Services.BluetoothLock.Tdo;
using Plugin.Permissions.Abstractions;
using Plugin.BLE.Abstractions.Contracts;
using TINK.MultilingualResources;
using Plugin.Permissions;
using TINK.Services.Permissions;
using TINK.Model.Station;
using TINK.Model.Device;
@ -59,7 +58,7 @@ namespace TINK.ViewModel.BikesAtStation
/// <param name="viewService">Interface to actuate methodes on GUI.</param>
public BikesAtStationPageViewModel(
User user,
IPermissions permissions,
ILocationPermission permissions,
IBluetoothLE bluetoothLE,
string runtimPlatform,
IStation selectedStation,
@ -81,6 +80,10 @@ namespace TINK.ViewModel.BikesAtStation
Title = string.Format(m_oStation?.StationName != null
? m_oStation.StationName
: string.Empty);
StationDetailText = string.Format(m_oStation?.Id != null
? string.Format(AppResources.MarkingBikesAtStationStationId, m_oStation.Id)
: string.Empty); ;
CollectionChanged += (sender, eventargs) =>
{
@ -121,8 +124,8 @@ namespace TINK.ViewModel.BikesAtStation
public string ContactSupportHintText
=> string.Format(AppResources.MarkingContactSupport, m_oStation?.OperatorData?.Name ?? "Operator");
/// <summary> Returns if info about the fact that user did not request or book any bikes is visible or not.<summary>
/// Gets message that logged in user has not booked any bikes.
/// <summary>
/// Returns if info about the fact that user did not request or book any bikes is visible or not.
/// </summary>
public bool IsNoBikesAtStationVisible
{
@ -207,6 +210,9 @@ namespace TINK.ViewModel.BikesAtStation
}
}
/// <summary> Returns detailed info about the station (station id).<summary>
public string StationDetailText { get; private set; }
/// <summary>
/// Invoked when page is shown.
/// Starts update process.
@ -233,10 +239,6 @@ namespace TINK.ViewModel.BikesAtStation
.Select(x => x.LockInfo)
.ToList();
Title = string.Format(m_oStation?.StationName != null
? m_oStation.StationName
: string.Empty);
if (LockService is ILocksServiceFake serviceFake)
{
serviceFake.UpdateSimulation(bikesAtStation);
@ -248,12 +250,12 @@ namespace TINK.ViewModel.BikesAtStation
if (bikesAtStation.GetLockIt().Count > 0
&& RuntimePlatform == Device.Android)
{
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
var status = await PermissionsService.CheckStatusAsync();
if (status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,

View file

@ -15,18 +15,12 @@ using System.Collections.ObjectModel;
#if USEFLYOUT
using TINK.View.MasterDetail;
#endif
using TINK.Settings;
using TINK.Model.Connector;
using TINK.Model.Services.CopriApi;
using Plugin.Permissions;
using TINK.Services.Permissions;
using Xamarin.Essentials;
using System.Threading;
using TINK.MultilingualResources;
using TINK.Services.BluetoothLock;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.ViewModel.Info;
using TINK.Repository;
using Plugin.Permissions.Abstractions;
using TINK.Model.Services.Geolocation;
namespace TINK.ViewModel.Contact
@ -48,7 +42,7 @@ namespace TINK.ViewModel.Contact
/// <summary>
/// Service to query/ manage permissions (location) of the app.
/// </summary>
private IPermissions PermissionsService { get; }
private ILocationPermission PermissionsService { get; }
/// <summary>
/// Service to manage bluetooth stack.
@ -114,7 +108,7 @@ namespace TINK.ViewModel.Contact
/// <param name="navigation">Interface to navigate.</param>
public SelectStationPageViewModel(
ITinkApp tinkApp,
IPermissions permissionsService,
ILocationPermission permissionsService,
Plugin.BLE.Abstractions.Contracts.IBluetoothLE bluetoothService,
IGeolocation geolocationService,
Action<MapSpan> moveToRegionDelegate,
@ -288,14 +282,14 @@ namespace TINK.ViewModel.Contact
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
var status = await PermissionsService.CheckStatusAsync();
if (TinkApp.CenterMapToCurrentLocation
&& !GeolocationService.IsSimulation
&& status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
&& status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,

View file

@ -19,8 +19,7 @@ using TINK.Model;
using Xamarin.Forms;
using TINK.ViewModel.Bikes;
using TINK.Services.BluetoothLock.Tdo;
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;
using TINK.Services.Permissions;
using Plugin.BLE.Abstractions.Contracts;
using TINK.MultilingualResources;
using TINK.Model.Device;
@ -73,7 +72,7 @@ namespace TINK.ViewModel.FindBike
/// <param name="viewService">Interface to actuate methodes on GUI.</param>
public FindBikePageViewModel(
User p_oUser,
IPermissions permissions,
ILocationPermission permissions,
IBluetoothLE bluetoothLE,
string runtimPlatform,
Func<bool> isConnectedDelegate,
@ -144,12 +143,12 @@ namespace TINK.ViewModel.FindBike
&& RuntimePlatform == Device.Android)
{
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
var status = await PermissionsService.CheckStatusAsync();
if (status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,

View file

@ -18,7 +18,7 @@ using TINK.View.MasterDetail;
using TINK.Settings;
using TINK.Model.Connector;
using TINK.Model.Services.CopriApi;
using Plugin.Permissions;
using TINK.Services.Permissions;
using Xamarin.Essentials;
using System.Threading;
using TINK.MultilingualResources;
@ -26,7 +26,6 @@ using TINK.Services.BluetoothLock;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.ViewModel.Info;
using TINK.Repository;
using Plugin.Permissions.Abstractions;
using TINK.Model.Services.Geolocation;
#if !TRYNOTBACKSTYLE
@ -51,7 +50,7 @@ namespace TINK.ViewModel.Map
/// <summary>
/// Service to query/ manage permissions (location) of the app.
/// </summary>
private IPermissions PermissionsService { get; }
private ILocationPermission PermissionsService { get; }
/// <summary>
/// Service to manage bluetooth stack.
@ -121,7 +120,7 @@ namespace TINK.ViewModel.Map
/// <param name="navigation">Interface to navigate.</param>
public MapPageViewModel(
ITinkApp tinkApp,
IPermissions permissionsService,
ILocationPermission permissionsService,
Plugin.BLE.Abstractions.Contracts.IBluetoothLE bluetoothService,
IGeolocation geolocationService,
Action<MapSpan> moveToRegionDelegate,
@ -318,14 +317,14 @@ namespace TINK.ViewModel.Map
ActionText = AppResources.ActivityTextRequestingLocationPermissions;
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
var status = await PermissionsService.CheckStatusAsync();
if (TinkApp.CenterMapToCurrentLocation
&& !GeolocationService.IsSimulation
&& status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
&& status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,
@ -345,22 +344,6 @@ namespace TINK.ViewModel.Map
}
}
// Move and scale before getting stations and bikes which takes some time.
ActionText = AppResources.ActivityTextCenterMap;
Location currentLocation = null;
try
{
currentLocation = TinkApp.CenterMapToCurrentLocation
? await GeolocationService.GetAsync()
: null;
}
catch (Exception ex)
{
Log.ForContext<MapPageViewModel>().Error("Getting location failed. {Exception}", ex);
}
MoveAndScale(m_oMoveToRegionDelegate, TinkApp.Uris.ActiveUri, ActiveFilterMap, currentLocation);
ActionText = AppResources.ActivityTextMapLoadingStationsAndBikes;
IsConnected = TinkApp.GetIsConnected();
var resultStationsAndBikes = await TinkApp.GetConnector(IsConnected).Query.GetBikesAndStationsAsync();
@ -435,6 +418,22 @@ namespace TINK.ViewModel.Map
// Update pins color form count of bikes located at station.
UpdatePinsColor(colors);
// Move and scale before getting stations and bikes which takes some time.
ActionText = AppResources.ActivityTextCenterMap;
Location currentLocation = null;
try
{
currentLocation = TinkApp.CenterMapToCurrentLocation
? await GeolocationService.GetAsync()
: null;
}
catch (Exception ex)
{
Log.ForContext<MapPageViewModel>().Error("Getting location failed. {Exception}", ex);
}
MoveAndScale(m_oMoveToRegionDelegate, TinkApp.Uris.ActiveUri, ActiveFilterMap, currentLocation);
m_oViewUpdateManager = CreateUpdateTask();
Log.ForContext<MapPageViewModel>().Verbose("Update pins color done.");
@ -854,14 +853,14 @@ namespace TINK.ViewModel.Map
Pins.Clear();
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
var status = await PermissionsService.CheckStatusAsync();
if (TinkApp.CenterMapToCurrentLocation
&& !GeolocationService.IsSimulation
&& status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
&& status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,

View file

@ -18,8 +18,7 @@ using TINK.Model;
using Xamarin.Forms;
using TINK.ViewModel.Bikes;
using TINK.Services.BluetoothLock.Tdo;
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;
using TINK.Services.Permissions;
using Plugin.BLE.Abstractions.Contracts;
using TINK.MultilingualResources;
using TINK.Model.Device;
@ -45,7 +44,7 @@ namespace TINK.ViewModel.MyBikes
/// <param name="viewService">Interface to actuate methodes on GUI.</param>
public MyBikesPageViewModel(
User p_oUser,
IPermissions permissions,
ILocationPermission permissions,
IBluetoothLE bluetoothLE,
string runtimPlatform,
Func<bool> isConnectedDelegate,
@ -119,12 +118,12 @@ namespace TINK.ViewModel.MyBikes
&& RuntimePlatform == Device.Android)
{
// Check location permission
var status = await PermissionsService.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
var status = await PermissionsService.CheckStatusAsync();
if (status != Status.Granted)
{
var permissionResult = await PermissionsService.RequestPermissionAsync<LocationPermission>();
var permissionResult = await PermissionsService.RequestAsync();
if (permissionResult != PermissionStatus.Granted)
if (permissionResult != Status.Granted)
{
var dialogResult = await ViewService.DisplayAlert(
AppResources.MessageTitleHint,

View file

@ -127,8 +127,9 @@ namespace TINK.ViewModel
Themes = new ServicesViewModel(
TinkApp.Themes.Select(x => x.GetType().FullName),
new Dictionary<string, string> {
{ typeof(Themes.Konrad).FullName, "Konrad" },
{ typeof(Themes.ShareeBike).FullName, "sharee.bike" }
{ typeof(Themes.Konrad).FullName, "Mein konrad" /* display name in picker */},
{ typeof(Themes.ShareeBike).FullName, "sharee.bike" /* display name in picker */},
{ typeof(Themes.LastenradBayern).FullName, "LastenradBayern" /* display name in picker */}
},
TinkApp.Themes.Active.GetType().FullName);
@ -185,7 +186,11 @@ namespace TINK.ViewModel
else if (Themes.Active == typeof(Themes.ShareeBike).FullName)
{
mergedDictionaries.Add(new Themes.ShareeBike());
}
}
else if (Themes.Active == typeof(Themes.LastenradBayern).FullName)
{
mergedDictionaries.Add(new Themes.LastenradBayern());
}
else
{
Log.ForContext<SettingsPageViewModel>().Debug($"No theme {Themes.Active} found.");