diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs index 47d1b6bef..43e956339 100644 --- a/src/Android/Services/DeviceActionService.cs +++ b/src/Android/Services/DeviceActionService.cs @@ -261,6 +261,21 @@ namespace Bit.Droid.Services return result.Task; } + public void RateApp() + { + var activity = (MainActivity)CrossCurrentActivity.Current.Activity; + try + { + var rateIntent = RateIntentForUrl("market://details", activity); + activity.StartActivity(rateIntent); + } + catch(ActivityNotFoundException) + { + var rateIntent = RateIntentForUrl("https://play.google.com/store/apps/details", activity); + activity.StartActivity(rateIntent); + } + } + private bool DeleteDir(Java.IO.File dir) { if(dir != null && dir.IsDirectory) @@ -315,5 +330,22 @@ namespace Bit.Droid.Services } return intents; } + + private Intent RateIntentForUrl(string url, Activity activity) + { + var intent = new Intent(Intent.ActionView, Android.Net.Uri.Parse($"{url}?id={activity.PackageName}")); + var flags = ActivityFlags.NoHistory | ActivityFlags.MultipleTask; + if((int)Build.VERSION.SdkInt >= 21) + { + flags |= ActivityFlags.NewDocument; + } + else + { + // noinspection deprecation + flags |= ActivityFlags.ClearWhenTaskReset; + } + intent.AddFlags(flags); + return intent; + } } } \ No newline at end of file diff --git a/src/App/Abstractions/IDeviceActionService.cs b/src/App/Abstractions/IDeviceActionService.cs index 2cbfb60d9..98def1c1d 100644 --- a/src/App/Abstractions/IDeviceActionService.cs +++ b/src/App/Abstractions/IDeviceActionService.cs @@ -16,5 +16,6 @@ namespace Bit.App.Abstractions Task SelectFileAsync(); Task DisplayPromptAync(string title = null, string description = null, string text = null, string okButtonText = null, string cancelButtonText = null); + void RateApp(); } } \ No newline at end of file diff --git a/src/App/Pages/Settings/SettingsPage.xaml.cs b/src/App/Pages/Settings/SettingsPage.xaml.cs index 5e93c837d..91489e7f1 100644 --- a/src/App/Pages/Settings/SettingsPage.xaml.cs +++ b/src/App/Pages/Settings/SettingsPage.xaml.cs @@ -52,6 +52,10 @@ namespace Bit.App.Pages { await _vm.FingerprintAsync(); } + else if(item.Name == AppResources.RateTheApp) + { + _vm.Rate(); + } } } } diff --git a/src/App/Pages/Settings/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPageViewModel.cs index f9f4b8620..55f1b1f03 100644 --- a/src/App/Pages/Settings/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPageViewModel.cs @@ -1,4 +1,5 @@ -using Bit.App.Resources; +using Bit.App.Abstractions; +using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Utilities; using System; @@ -13,12 +14,14 @@ namespace Bit.App.Pages private readonly IPlatformUtilsService _platformUtilsService; private readonly ICryptoService _cryptoService; private readonly IUserService _userService; + private readonly IDeviceActionService _deviceActionService; public SettingsPageViewModel() { _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _cryptoService = ServiceContainer.Resolve("cryptoService"); _userService = ServiceContainer.Resolve("userService"); + _deviceActionService = ServiceContainer.Resolve("deviceActionService"); PageTitle = AppResources.Settings; BuildList(); @@ -57,6 +60,11 @@ namespace Bit.App.Pages } } + public void Rate() + { + _deviceActionService.RateApp(); + } + private void BuildList() { var doUpper = Device.RuntimePlatform != Device.Android; diff --git a/src/iOS/Services/DeviceActionService.cs b/src/iOS/Services/DeviceActionService.cs index 67ba1c9df..89dcda04b 100644 --- a/src/iOS/Services/DeviceActionService.cs +++ b/src/iOS/Services/DeviceActionService.cs @@ -16,6 +16,7 @@ using Foundation; using MobileCoreServices; using Photos; using UIKit; +using Xamarin.Forms; namespace Bit.iOS.Services { @@ -197,6 +198,21 @@ namespace Bit.iOS.Services return result.Task; } + public void RateApp() + { + string uri = null; + if(SystemMajorVersion() < 11) + { + uri = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews" + + "?id=1137397744&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"; + } + else + { + uri = "itms-apps://itunes.apple.com/us/app/id1137397744?action=write-review"; + } + Device.OpenUri(new Uri(uri)); + } + private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e) { if(sender is UIImagePickerController picker) @@ -313,5 +329,16 @@ namespace Bit.iOS.Services var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); return Path.Combine(documents, "..", "tmp"); } + + private int SystemMajorVersion() + { + var versionParts = UIDevice.CurrentDevice.SystemVersion.Split('.'); + if(versionParts.Length > 0 && int.TryParse(versionParts[0], out int version)) + { + return version; + } + // unable to determine version + return -1; + } } } \ No newline at end of file