diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ed865b66..b233d9af9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -330,7 +330,7 @@ jobs: ios: name: Apple iOS - runs-on: macos-10.15 + runs-on: macos-11 steps: - name: Print environment run: | diff --git a/src/Android/Accessibility/AccessibilityActivity.cs b/src/Android/Accessibility/AccessibilityActivity.cs index f0e8d0e15..2be39b24f 100644 --- a/src/Android/Accessibility/AccessibilityActivity.cs +++ b/src/Android/Accessibility/AccessibilityActivity.cs @@ -9,7 +9,7 @@ using Bit.Core.Utilities; namespace Bit.Droid.Accessibility { - [Activity(Theme = "@style/LightTheme.Splash", WindowSoftInputMode = SoftInput.StateHidden)] + [Activity(Theme = "@style/BaseTheme", WindowSoftInputMode = SoftInput.StateHidden)] public class AccessibilityActivity : Activity { private DateTime? _lastLaunch = null; diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj index de1b03b29..3ba99a4d4 100644 --- a/src/Android/Android.csproj +++ b/src/Android/Android.csproj @@ -76,11 +76,11 @@ 1.8.10 - - - - - + + + + + 1.7.0 @@ -88,7 +88,7 @@ 122.0.0 - + 117.0.1 @@ -122,6 +122,8 @@ + + @@ -144,6 +146,7 @@ + @@ -171,8 +174,6 @@ - - @@ -185,6 +186,7 @@ + diff --git a/src/Android/Effects/FabShadowEffect.cs b/src/Android/Effects/FabShadowEffect.cs index 11bda2e4f..9901a51ba 100644 --- a/src/Android/Effects/FabShadowEffect.cs +++ b/src/Android/Effects/FabShadowEffect.cs @@ -1,6 +1,6 @@ using Android.Graphics.Drawables; -using Bit.App.Utilities; using Bit.Droid.Effects; +using Bit.Droid.Utilities; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -14,7 +14,7 @@ namespace Bit.Droid.Effects if (Control is Android.Widget.Button button) { var gd = new GradientDrawable(); - gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid()); + gd.SetColor(ThemeHelpers.FabColor); gd.SetCornerRadius(100); button.SetBackground(gd); diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 3208295f4..7d2023606 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -15,9 +15,9 @@ using Bit.Droid.Receivers; using Bit.App.Models; using Bit.Core.Enums; using Android.Nfc; -using Bit.App.Utilities; using System.Threading.Tasks; using AndroidX.Core.Content; +using Bit.App.Utilities; using ZXing.Net.Mobile.Android; namespace Bit.Droid @@ -30,7 +30,7 @@ namespace Bit.Droid LaunchMode = LaunchMode.SingleTask, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | - ConfigChanges.Navigation)] + ConfigChanges.Navigation | ConfigChanges.UiMode)] [IntentFilter( new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, @@ -81,7 +81,6 @@ namespace Bit.Droid TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; - UpdateTheme(ThemeManager.GetTheme(true)); base.OnCreate(savedInstanceState); if (!CoreHelpers.InDebugMode()) { @@ -118,7 +117,7 @@ namespace Bit.Droid } else if (message.Command == "updatedTheme") { - RestartApp(); + Xamarin.Forms.Device.BeginInvokeOnMainThread(() => AppearanceAdjustments()); } else if (message.Command == "exit") { @@ -141,6 +140,7 @@ namespace Bit.Droid { base.OnResume(); Xamarin.Essentials.Platform.OnResume(); + AppearanceAdjustments(); if (_deviceActionService.SupportsNfc()) { try @@ -382,45 +382,10 @@ namespace Bit.Droid } } - private void UpdateTheme(string theme) + private void AppearanceAdjustments() { - if (theme == "light") - { - SetTheme(Resource.Style.LightTheme); - } - else if (theme == "dark") - { - SetTheme(Resource.Style.DarkTheme); - } - else if (theme == "black") - { - SetTheme(Resource.Style.BlackTheme); - } - else if (theme == "nord") - { - SetTheme(Resource.Style.NordTheme); - } - else - { - if (_deviceActionService.UsingDarkTheme()) - { - SetTheme(Resource.Style.DarkTheme); - } - else - { - SetTheme(Resource.Style.LightTheme); - } - } - } - - private void RestartApp() - { - var intent = new Intent(this, typeof(MainActivity)); - var pendingIntent = PendingIntent.GetActivity(this, 5923650, intent, PendingIntentFlags.CancelCurrent); - var alarmManager = GetSystemService(AlarmService) as AlarmManager; - var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + 500; - alarmManager.Set(AlarmType.Rtc, triggerMs, pendingIntent); - Java.Lang.JavaSystem.Exit(0); + Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor); + ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled()); } private void ExitApp() diff --git a/src/Android/Renderers/CustomEditorRenderer.cs b/src/Android/Renderers/CustomEditorRenderer.cs index edadb4c54..035c2f3eb 100644 --- a/src/Android/Renderers/CustomEditorRenderer.cs +++ b/src/Android/Renderers/CustomEditorRenderer.cs @@ -1,6 +1,9 @@ -using Android.Content; +using System.ComponentModel; +using Android.Content; +using Android.Content.Res; using Android.Views.InputMethods; using Bit.Droid.Renderers; +using Bit.Droid.Utilities; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -25,6 +28,7 @@ namespace Bit.Droid.Renderers protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); + UpdateBorderColor(); if (Control != null && e.NewElement != null) { Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight, @@ -33,5 +37,33 @@ namespace Bit.Droid.Renderers (ImeAction)ImeFlags.NoExtractUi; } } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Entry.TextColorProperty.PropertyName) + { + UpdateBorderColor(); + } + } + + private void UpdateBorderColor() + { + if (Control != null) + { + var states = new[] + { + new[] { Android.Resource.Attribute.StateFocused }, // focused + new[] { -Android.Resource.Attribute.StateFocused }, // unfocused + }; + var colors = new int[] + { + ThemeHelpers.PrimaryColor, + ThemeHelpers.MutedColor + }; + Control.BackgroundTintList = new ColorStateList(states, colors); + } + } } } diff --git a/src/Android/Renderers/CustomEntryRenderer.cs b/src/Android/Renderers/CustomEntryRenderer.cs index 219b5aa17..fd17aa108 100644 --- a/src/Android/Renderers/CustomEntryRenderer.cs +++ b/src/Android/Renderers/CustomEntryRenderer.cs @@ -1,10 +1,12 @@ using System.ComponentModel; using Android.Content; +using Android.Content.Res; using Android.Graphics; using Android.Text; using Android.Views.InputMethods; using Android.Widget; using Bit.Droid.Renderers; +using Bit.Droid.Utilities; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -20,6 +22,7 @@ namespace Bit.Droid.Renderers protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); + UpdateBorderColor(); if (Control != null && e.NewElement != null) { Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight, @@ -68,6 +71,28 @@ namespace Bit.Droid.Renderers } } } + else if (e.PropertyName == Entry.TextColorProperty.PropertyName) + { + UpdateBorderColor(); + } + } + + private void UpdateBorderColor() + { + if (Control != null) + { + var states = new[] + { + new[] { Android.Resource.Attribute.StateFocused }, // focused + new[] { -Android.Resource.Attribute.StateFocused }, // unfocused + }; + var colors = new int[] + { + ThemeHelpers.PrimaryColor, + ThemeHelpers.MutedColor + }; + Control.BackgroundTintList = new ColorStateList(states, colors); + } } } } diff --git a/src/Android/Renderers/CustomPickerRenderer.cs b/src/Android/Renderers/CustomPickerRenderer.cs index 2327072d4..ba0d60fe2 100644 --- a/src/Android/Renderers/CustomPickerRenderer.cs +++ b/src/Android/Renderers/CustomPickerRenderer.cs @@ -1,5 +1,8 @@ -using Android.Content; +using System.ComponentModel; +using Android.Content; +using Android.Content.Res; using Bit.Droid.Renderers; +using Bit.Droid.Utilities; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -15,11 +18,40 @@ namespace Bit.Droid.Renderers protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); + UpdateBorderColor(); if (Control != null && e.NewElement != null) { Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight, Control.PaddingBottom + 20); } } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Picker.TextColorProperty.PropertyName) + { + UpdateBorderColor(); + } + } + + private void UpdateBorderColor() + { + if (Control != null) + { + var states = new[] + { + new[] { Android.Resource.Attribute.StateFocused }, // focused + new[] { -Android.Resource.Attribute.StateFocused }, // unfocused + }; + var colors = new int[] + { + ThemeHelpers.PrimaryColor, + ThemeHelpers.MutedColor + }; + Control.BackgroundTintList = new ColorStateList(states, colors); + } + } } } diff --git a/src/Android/Renderers/CustomSwitchRenderer.cs b/src/Android/Renderers/CustomSwitchRenderer.cs new file mode 100644 index 000000000..121f152ca --- /dev/null +++ b/src/Android/Renderers/CustomSwitchRenderer.cs @@ -0,0 +1,59 @@ +using System.ComponentModel; +using Android.Content; +using Android.Content.Res; +using Android.Graphics.Drawables; +using AndroidX.Core.Content.Resources; +using Bit.Droid.Renderers; +using Bit.Droid.Utilities; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; + +[assembly: ExportRenderer(typeof(Switch), typeof(CustomSwitchRenderer))] +namespace Bit.Droid.Renderers +{ + public class CustomSwitchRenderer : SwitchRenderer + { + public CustomSwitchRenderer(Context context) + : base(context) + {} + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + UpdateColors(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Switch.OnColorProperty.PropertyName) + { + UpdateColors(); + } + } + + private void UpdateColors() + { + if (Control != null) + { + var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null); + if (t is GradientDrawable thumb) + { + Control.ThumbDrawable = thumb; + } + var thumbStates = new[] + { + new[] { Android.Resource.Attribute.StateChecked }, // checked + new[] { -Android.Resource.Attribute.StateChecked }, // unchecked + }; + var thumbColors = new int[] + { + ThemeHelpers.SwitchOnColor, + ThemeHelpers.SwitchThumbColor + }; + Control.ThumbTintList = new ColorStateList(thumbStates, thumbColors); + } + } + } +} diff --git a/src/Android/Renderers/ExtendedGridRenderer.cs b/src/Android/Renderers/ExtendedGridRenderer.cs index 050e0f011..a46cbca52 100644 --- a/src/Android/Renderers/ExtendedGridRenderer.cs +++ b/src/Android/Renderers/ExtendedGridRenderer.cs @@ -1,6 +1,5 @@ using Android.Content; using Bit.App.Controls; -using Bit.App.Utilities; using Bit.Droid.Renderers; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -10,8 +9,6 @@ namespace Bit.Droid.Renderers { public class ExtendedGridRenderer : ViewRenderer { - private static int? _bgResId; - public ExtendedGridRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs elementChangedEvent) @@ -19,25 +16,8 @@ namespace Bit.Droid.Renderers base.OnElementChanged(elementChangedEvent); if (elementChangedEvent.NewElement != null) { - SetBackgroundResource(GetBgResId()); + SetBackgroundResource(Resource.Drawable.list_item_bg); } } - - private int GetBgResId() - { - if (_bgResId == null) - { - if (ThemeManager.GetTheme(true) == "nord") - { - _bgResId = Resource.Drawable.list_item_bg_nord; - } - else - { - _bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg : - Resource.Drawable.list_item_bg_dark; - } - } - return _bgResId.Value; - } } } diff --git a/src/Android/Renderers/ExtendedSliderRenderer.cs b/src/Android/Renderers/ExtendedSliderRenderer.cs index aee0ffc63..0ff6fa9f3 100644 --- a/src/Android/Renderers/ExtendedSliderRenderer.cs +++ b/src/Android/Renderers/ExtendedSliderRenderer.cs @@ -1,4 +1,5 @@ -using Android.Content; +using System.ComponentModel; +using Android.Content; using Android.Graphics.Drawables; using AndroidX.Core.Content.Resources; using Bit.App.Controls; @@ -18,6 +19,21 @@ namespace Bit.Droid.Renderers protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); + UpdateColor(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == ExtendedSlider.ThumbBorderColorProperty.PropertyName) + { + UpdateColor(); + } + } + + private void UpdateColor() + { if (Control != null && Element is ExtendedSlider view) { var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null); diff --git a/src/Android/Renderers/ExtendedStackLayoutRenderer.cs b/src/Android/Renderers/ExtendedStackLayoutRenderer.cs index 7242ef825..d28668a4a 100644 --- a/src/Android/Renderers/ExtendedStackLayoutRenderer.cs +++ b/src/Android/Renderers/ExtendedStackLayoutRenderer.cs @@ -1,6 +1,5 @@ using Android.Content; using Bit.App.Controls; -using Bit.App.Utilities; using Bit.Droid.Renderers; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -10,8 +9,6 @@ namespace Bit.Droid.Renderers { public class ExtendedStackLayoutRenderer : ViewRenderer { - private static int? _bgResId; - public ExtendedStackLayoutRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs elementChangedEvent) @@ -19,25 +16,8 @@ namespace Bit.Droid.Renderers base.OnElementChanged(elementChangedEvent); if (elementChangedEvent.NewElement != null) { - SetBackgroundResource(GetBgResId()); + SetBackgroundResource(Resource.Drawable.list_item_bg); } } - - private int GetBgResId() - { - if (_bgResId == null) - { - if (ThemeManager.GetTheme(true) == "nord") - { - _bgResId = Resource.Drawable.list_item_bg_nord; - } - else - { - _bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg : - Resource.Drawable.list_item_bg_dark; - } - } - return _bgResId.Value; - } } } diff --git a/src/Android/Renderers/ExtendedStepperRenderer.cs b/src/Android/Renderers/ExtendedStepperRenderer.cs new file mode 100644 index 000000000..656eb46b9 --- /dev/null +++ b/src/Android/Renderers/ExtendedStepperRenderer.cs @@ -0,0 +1,72 @@ +using System.ComponentModel; +using Android.Content; +using Android.Graphics; +using Android.OS; +using Bit.App.Controls; +using Bit.Droid.Renderers; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; + +[assembly: ExportRenderer(typeof(ExtendedStepper), typeof(ExtendedStepperRenderer))] +namespace Bit.Droid.Renderers +{ + public class ExtendedStepperRenderer : StepperRenderer + { + public ExtendedStepperRenderer(Context context) + : base(context) + {} + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + UpdateBgColor(); + UpdateFgColor(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == ExtendedStepper.StepperBackgroundColorProperty.PropertyName) + { + UpdateBgColor(); + } + else if (e.PropertyName == ExtendedStepper.StepperForegroundColorProperty.PropertyName) + { + UpdateFgColor(); + } + } + + private void UpdateBgColor() + { + if (Control != null && Element is ExtendedStepper view) + { + if (Build.VERSION.SdkInt >= BuildVersionCodes.Q) + { + Control.GetChildAt(0)?.Background?.SetColorFilter( + new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply)); + Control.GetChildAt(1)?.Background?.SetColorFilter( + new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply)); + } + else + { + Control.GetChildAt(0)?.Background?.SetColorFilter( + view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply); + Control.GetChildAt(1)?.Background?.SetColorFilter( + view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply); + } + } + } + + private void UpdateFgColor() + { + if (Control != null && Element is ExtendedStepper view) + { + var btn0 = Control.GetChildAt(0) as Android.Widget.Button; + btn0?.SetTextColor(view.StepperForegroundColor.ToAndroid()); + var btn1 = Control.GetChildAt(1) as Android.Widget.Button; + btn1?.SetTextColor(view.StepperForegroundColor.ToAndroid()); + } + } + } +} diff --git a/src/Android/Resources/drawable-v23/splash_screen.xml b/src/Android/Resources/drawable-v23/splash_screen.xml index 2b50133a9..fe987e71d 100644 --- a/src/Android/Resources/drawable-v23/splash_screen.xml +++ b/src/Android/Resources/drawable-v23/splash_screen.xml @@ -6,7 +6,7 @@ TODO: When API 23 becomes our new minimum, replace 'splash_screen.xml' in 'drawa --> - + + android:color="#8E8E93"> - - - \ No newline at end of file diff --git a/src/Android/Resources/drawable/list_item_bg_nord.xml b/src/Android/Resources/drawable/list_item_bg_nord.xml deleted file mode 100644 index d9e9f0c75..000000000 --- a/src/Android/Resources/drawable/list_item_bg_nord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Android/Resources/drawable/splash_screen.xml b/src/Android/Resources/drawable/splash_screen.xml index aaf7619dc..56530dc15 100644 --- a/src/Android/Resources/drawable/splash_screen.xml +++ b/src/Android/Resources/drawable/splash_screen.xml @@ -1,7 +1,7 @@  - + diff --git a/src/Android/Resources/drawable/switch_thumb.xml b/src/Android/Resources/drawable/switch_thumb.xml new file mode 100644 index 000000000..ea043d6bd --- /dev/null +++ b/src/Android/Resources/drawable/switch_thumb.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Android/Resources/values-night/styles.xml b/src/Android/Resources/values-night/styles.xml index 58dda976a..7e5a4074e 100644 --- a/src/Android/Resources/values-night/styles.xml +++ b/src/Android/Resources/values-night/styles.xml @@ -1,8 +1,26 @@ - + + + + diff --git a/src/Android/Resources/values/colors.xml b/src/Android/Resources/values/colors.xml index 5bbfa662c..a75733e20 100644 --- a/src/Android/Resources/values/colors.xml +++ b/src/Android/Resources/values/colors.xml @@ -1,38 +1,19 @@ - + #175DDC #1A3B66 #175DDC #1452BC #dddddd - #bbbbbb - + #52bdfb #191919 - #191919 + #666666 #191919 - - #282828 - - - #3b4252 - #e5e9f0 - #81a1c1 - #2e3440 - #e5e9f0 - #20242D - #3b4252 - #2e3440 - #4c566a - #e5e9f0 - #4c566a - #20242D - - #FFFFFF #FFFFFF #000000 #333333 diff --git a/src/Android/Resources/values/styles.xml b/src/Android/Resources/values/styles.xml index 07e4e2866..6b0b37f0b 100644 --- a/src/Android/Resources/values/styles.xml +++ b/src/Android/Resources/values/styles.xml @@ -1,21 +1,12 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs index 45fd32f27..35202e7e4 100644 --- a/src/Android/Services/DeviceActionService.cs +++ b/src/Android/Services/DeviceActionService.cs @@ -8,7 +8,6 @@ using Android.App; using Android.App.Assist; using Android.Content; using Android.Content.PM; -using Android.Content.Res; using Android.Nfc; using Android.OS; using Android.Provider; @@ -641,18 +640,11 @@ namespace Bit.Droid.Services public bool AutofillAccessibilityServiceRunning() { - try - { - var activity = (MainActivity)CrossCurrentActivity.Current.Activity; - var manager = activity.GetSystemService(Context.ActivityService) as ActivityManager; - var services = manager.GetRunningServices(int.MaxValue); - return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") && - s.Service.ClassName.ToLowerInvariant().Contains("accessibilityservice")); - } - catch - { - return false; - } + var activity = (MainActivity)CrossCurrentActivity.Current.Activity; + var enabledServices = Settings.Secure.GetString(activity.ContentResolver, + Settings.Secure.EnabledAccessibilityServices); + return Application.Context.PackageName != null && + (enabledServices?.Contains(Application.Context.PackageName) ?? false); } public bool AutofillAccessibilityOverlayPermitted() @@ -741,21 +733,6 @@ namespace Bit.Droid.Services } } - public bool UsingDarkTheme() - { - try - { - if (Build.VERSION.SdkInt >= BuildVersionCodes.Q) - { - var app = CrossCurrentActivity.Current.AppContext; - var uiModeFlags = app.Resources.Configuration.UiMode & UiMode.NightMask; - return uiModeFlags == UiMode.NightYes; - } - } - catch { } - return false; - } - public long GetActiveTime() { // Returns milliseconds since the system was booted, and includes deep sleep. This clock is guaranteed to diff --git a/src/Android/Utilities/ThemeHelpers.cs b/src/Android/Utilities/ThemeHelpers.cs new file mode 100644 index 000000000..3dfac9efa --- /dev/null +++ b/src/Android/Utilities/ThemeHelpers.cs @@ -0,0 +1,67 @@ +using Android.Graphics; +using Bit.App.Utilities; +using Xamarin.Forms.Platform.Android; + +namespace Bit.Droid.Utilities +{ + public class ThemeHelpers + { + public static bool LightTheme = true; + + public static Color PrimaryColor + { + get => ThemeManager.GetResourceColor("PrimaryColor").ToAndroid(); + } + public static Color MutedColor + { + get => ThemeManager.GetResourceColor("MutedColor").ToAndroid(); + } + public static Color NavBarBackgroundColor + { + get => ThemeManager.GetResourceColor("NavigationBarBackgroundColor").ToAndroid(); + } + public static Color FabColor + { + get => ThemeManager.GetResourceColor("FabColor").ToAndroid(); + } + public static Color SwitchOnColor + { + get => ThemeManager.GetResourceColor("SwitchOnColor").ToAndroid(); + } + public static Color SwitchThumbColor + { + get => ThemeManager.GetResourceColor("SwitchThumbColor").ToAndroid(); + } + + public static void SetAppearance(string theme, bool osDarkModeEnabled) + { + SetThemeVariables(theme, osDarkModeEnabled); + } + + public static int GetDialogTheme() + { + if (LightTheme) + { + return Android.Resource.Style.ThemeMaterialLightDialog; + } + return Android.Resource.Style.ThemeMaterialDialog; + } + + private static void SetThemeVariables(string theme, bool osDarkModeEnabled) + { + if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled) + { + theme = "dark"; + } + + if (theme == "dark" || theme == "black" || theme == "nord") + { + LightTheme = false; + } + else + { + LightTheme = true; + } + } + } +} diff --git a/src/App/Abstractions/IDeviceActionService.cs b/src/App/Abstractions/IDeviceActionService.cs index 3c78759ba..e9d2b7dfb 100644 --- a/src/App/Abstractions/IDeviceActionService.cs +++ b/src/App/Abstractions/IDeviceActionService.cs @@ -42,7 +42,6 @@ namespace Bit.App.Abstractions void OpenAccessibilitySettings(); void OpenAccessibilityOverlayPermissionSettings(); void OpenAutofillSettings(); - bool UsingDarkTheme(); long GetActiveTime(); void CloseMainApp(); bool SupportsFido2(); diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index aff7401d7..f3b80e68f 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -200,6 +200,7 @@ namespace Bit.App private async void ResumedAsync() { + UpdateTheme(); await _vaultTimeoutService.CheckVaultTimeoutAsync(); _messagingService.Send("startEventTimer"); await ClearCacheIfNeededAsync(); @@ -337,6 +338,10 @@ namespace Bit.App InitializeComponent(); SetCulture(); ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources); + Current.RequestedThemeChanged += (s, a) => + { + UpdateTheme(); + }; Current.MainPage = new HomePage(); var mainPageTask = SetMainPageAsync(); ServiceContainer.Resolve("platformUtilsService").Init(); @@ -359,6 +364,15 @@ namespace Bit.App }); } + private void UpdateTheme() + { + Device.BeginInvokeOnMainThread(() => + { + ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources); + _messagingService.Send("updatedTheme"); + }); + } + private async Task LockedAsync(bool autoPromptBiometric) { await _stateService.PurgeAsync(); diff --git a/src/App/Controls/ExtendedSlider.cs b/src/App/Controls/ExtendedSlider.cs index 00aa8fab8..7d673fab9 100644 --- a/src/App/Controls/ExtendedSlider.cs +++ b/src/App/Controls/ExtendedSlider.cs @@ -5,7 +5,7 @@ namespace Bit.App.Controls public class ExtendedSlider : Slider { public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create( - nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Gray); + nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Default); public Color ThumbBorderColor { diff --git a/src/App/Controls/ExtendedStepper.cs b/src/App/Controls/ExtendedStepper.cs new file mode 100644 index 000000000..68191ae20 --- /dev/null +++ b/src/App/Controls/ExtendedStepper.cs @@ -0,0 +1,25 @@ +using Xamarin.Forms; + +namespace Bit.App.Controls +{ + public class ExtendedStepper : Stepper + { + public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create( + nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default); + + public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create( + nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default); + + public Color StepperBackgroundColor + { + get => (Color)GetValue(StepperBackgroundColorProperty); + set => SetValue(StepperBackgroundColorProperty, value); + } + + public Color StepperForegroundColor + { + get => (Color)GetValue(StepperForegroundColorProperty); + set => SetValue(StepperForegroundColorProperty, value); + } + } +} diff --git a/src/App/Pages/Accounts/HomePage.xaml b/src/App/Pages/Accounts/HomePage.xaml index 62896ba2f..1040914ad 100644 --- a/src/App/Pages/Accounts/HomePage.xaml +++ b/src/App/Pages/Accounts/HomePage.xaml @@ -40,11 +40,12 @@ HorizontalTextAlignment="Center"> + StyleClass="btn-primary" + Clicked="LogIn_Clicked" /> + Clicked="Register_Clicked" /> + Clicked="LogInSso_Clicked" /> diff --git a/src/App/Pages/Accounts/HomePage.xaml.cs b/src/App/Pages/Accounts/HomePage.xaml.cs index 204f389a1..a51185d13 100644 --- a/src/App/Pages/Accounts/HomePage.xaml.cs +++ b/src/App/Pages/Accounts/HomePage.xaml.cs @@ -13,11 +13,13 @@ namespace Bit.App.Pages private readonly HomeViewModel _vm; private readonly AppOptions _appOptions; private IMessagingService _messagingService; + private IBroadcasterService _broadcasterService; public HomePage(AppOptions appOptions = null) { _messagingService = ServiceContainer.Resolve("messagingService"); _messagingService.Send("showStatusBar", false); + _broadcasterService = ServiceContainer.Resolve("broadcasterService"); _appOptions = appOptions; InitializeComponent(); _vm = BindingContext as HomeViewModel; @@ -26,7 +28,7 @@ namespace Bit.App.Pages _vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync()); _vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync()); _vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync()); - _logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png"; + UpdateLogo(); } public async Task DismissRegisterPageAndLogInAsync(string email) @@ -39,6 +41,27 @@ namespace Bit.App.Pages { base.OnAppearing(); _messagingService.Send("showStatusBar", false); + _broadcasterService.Subscribe(nameof(HomePage), async (message) => + { + if (message.Command == "updatedTheme") + { + Device.BeginInvokeOnMainThread(() => + { + UpdateLogo(); + }); + } + }); + } + + protected override void OnDisappearing() + { + base.OnDisappearing(); + _broadcasterService.Unsubscribe(nameof(HomePage)); + } + + private void UpdateLogo() + { + _logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png"; } private void Close_Clicked(object sender, EventArgs e) diff --git a/src/App/Pages/Accounts/LockPage.xaml b/src/App/Pages/Accounts/LockPage.xaml index 5bb281f1f..22e5220f8 100644 --- a/src/App/Pages/Accounts/LockPage.xaml +++ b/src/App/Pages/Accounts/LockPage.xaml @@ -120,7 +120,9 @@ IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" /> - + + StyleClass="btn-primary" + Clicked="LogIn_Clicked"> diff --git a/src/App/Pages/BaseContentPage.cs b/src/App/Pages/BaseContentPage.cs index 8919f8e1d..ce844e183 100644 --- a/src/App/Pages/BaseContentPage.cs +++ b/src/App/Pages/BaseContentPage.cs @@ -4,6 +4,7 @@ using Bit.Core.Utilities; using System; using System.Threading.Tasks; using Bit.App.Abstractions; +using Bit.App.Utilities; using Xamarin.Forms; using Xamarin.Forms.PlatformConfiguration; using Xamarin.Forms.PlatformConfiguration.iOSSpecific; @@ -52,7 +53,8 @@ namespace Bit.App.Pages { IsRunning = true, VerticalOptions = LayoutOptions.CenterAndExpand, - HorizontalOptions = LayoutOptions.Center + HorizontalOptions = LayoutOptions.Center, + Color = ThemeManager.GetResourceColor("PrimaryColor"), }; if (targetView != null) { diff --git a/src/App/Pages/Generator/GeneratorPage.xaml b/src/App/Pages/Generator/GeneratorPage.xaml index 6824140b3..cdc241044 100644 --- a/src/App/Pages/Generator/GeneratorPage.xaml +++ b/src/App/Pages/Generator/GeneratorPage.xaml @@ -71,6 +71,7 @@ HorizontalOptions="CenterAndExpand" LineBreakMode="CharacterWrap" /> @@ -289,20 +243,20 @@ Text="{u:I18n Options}" x:Name="_btnOptions" StyleClass="box-row-button" - TextColor="{StaticResource PrimaryColor}" + TextColor="{DynamicResource PrimaryColor}" Margin="0" /> @@ -421,7 +375,7 @@ MaxLength="9" TextChanged="OnMaxAccessCountTextChanged" HorizontalOptions="FillAndExpand" /> - SendOptionsCommand { get; set; } public bool LoadedOnce { get; set; } - public async Task InitAsync() - { - SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync(); - } - public async Task LoadAsync() { if (_doingLoad) @@ -142,6 +137,7 @@ namespace Bit.App.Pages ShowNoData = false; Loading = true; ShowList = false; + SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync(); var groupedSends = new List(); var page = Page as SendGroupingsPage; diff --git a/src/App/Pages/Settings/AutofillServicesPage.xaml b/src/App/Pages/Settings/AutofillServicesPage.xaml index 03dc7bb83..15f5c9b2a 100644 --- a/src/App/Pages/Settings/AutofillServicesPage.xaml +++ b/src/App/Pages/Settings/AutofillServicesPage.xaml @@ -29,7 +29,7 @@ HorizontalOptions="End" />