diff --git a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs index b7291125d35a..eed968e65145 100644 --- a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs +++ b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs @@ -129,6 +129,18 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo } #else handlersCollection.AddHandler(); +#endif +#if ANDROID + if (RuntimeFeature.IsMaterial3Enabled) + { + handlersCollection.AddHandler(); + } + else + { + handlersCollection.AddHandler(); + } +#else + handlersCollection.AddHandler(); #endif handlersCollection.AddHandler(); handlersCollection.AddHandler(); @@ -137,7 +149,6 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo handlersCollection.AddHandler(); handlersCollection.AddHandler(); handlersCollection.AddHandler(); - handlersCollection.AddHandler(); handlersCollection.AddHandler(); handlersCollection.AddHandler(); handlersCollection.AddHandler(); diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png new file mode 100644 index 000000000000..00389027f079 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png new file mode 100644 index 000000000000..9583e30eeb13 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png new file mode 100644 index 000000000000..9eb5b1a6be7d Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png new file mode 100644 index 000000000000..caa56f9260ac Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png new file mode 100644 index 000000000000..0dfced6f2c75 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png new file mode 100644 index 000000000000..7ad732cac3ec Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs new file mode 100644 index 000000000000..70b972c40f5d --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs @@ -0,0 +1,107 @@ +// Material3 ProgressBar tests reuse the existing ProgressBar Feature Matrix HostApp page. +// The native Android view differs (LinearProgressIndicator vs ProgressBar), so these tests +// produce separate screenshot baselines under the Material3 category. +#if ANDROID +using System; +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests; + +public class Material3ProgressBarFeatureTests : _GalleryUITest +{ + public override string GalleryPageName => "ProgressBar Feature Matrix"; + + public Material3ProgressBarFeatureTests(TestDevice device) + : base(device) + { + } + + [Test, Order(1)] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_ProgressToMethod_VerifyVisualState() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("ProgressToEntry"); + App.ClearText("ProgressToEntry"); + App.EnterText("ProgressToEntry", "0.90"); + App.PressEnter(); + App.WaitForElement("ProgressToButton"); + App.Tap("ProgressToButton"); + Task.Delay(1000).Wait(); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } + + [Test] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_SetProgressOutOfRange() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("ProgressEntry"); + App.ClearText("ProgressEntry"); + App.EnterText("ProgressEntry", "1.44"); + App.PressEnter(); + App.WaitForElement("ProgressBarControl"); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } + + [Test] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_SetProgressNegativeValue() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("ProgressEntry"); + App.ClearText("ProgressEntry"); + App.EnterText("ProgressEntry", "-0.44"); + App.PressEnter(); + App.WaitForElement("ProgressBarControl"); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } + + [Test] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("ProgressEntry"); + App.ClearText("ProgressEntry"); + App.EnterText("ProgressEntry", "0.60"); + App.PressEnter(); + App.WaitForElement("ProgressColorRedButton"); + App.Tap("ProgressColorRedButton"); + App.WaitForElement("BackgroundColorLightBlueButton"); + App.Tap("BackgroundColorLightBlueButton"); + App.WaitForElement("ProgressBarControl"); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } + + [Test] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("FlowDirectionRTL"); + App.Tap("FlowDirectionRTL"); + App.WaitForElement("ProgressBarControl"); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } + + [Test] + [Category(UITestCategories.Material3)] + public void Material3ProgressBar_ToggleShadow_VerifyVisualState() + { + App.WaitForElement("ResetButton"); + App.Tap("ResetButton"); + App.WaitForElement("ShadowTrueRadio"); + App.Tap("ShadowTrueRadio"); + App.WaitForElement("ProgressBarControl"); + VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2)); + } +} +#endif diff --git a/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs new file mode 100644 index 000000000000..5cbe7e7f7c5f --- /dev/null +++ b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs @@ -0,0 +1,16 @@ +using Google.Android.Material.ProgressIndicator; + +namespace Microsoft.Maui.Handlers; + +// TODO: Material3 - make it public in .net 11 +internal class ProgressBarHandler2 : ProgressBarHandler +{ + protected override LinearProgressIndicator CreatePlatformView() + { + return new LinearProgressIndicator(MauiMaterialContextThemeWrapper.Create(Context)) + { + Indeterminate = false, + Max = ProgressBarExtensions.Maximum + }; + } +} diff --git a/src/Core/src/Platform/Android/ProgressBarExtensions.cs b/src/Core/src/Platform/Android/ProgressBarExtensions.cs index 14934bc981d6..c110c893b5c5 100644 --- a/src/Core/src/Platform/Android/ProgressBarExtensions.cs +++ b/src/Core/src/Platform/Android/ProgressBarExtensions.cs @@ -1,4 +1,5 @@ using Android.Content.Res; +using Google.Android.Material.ProgressIndicator; using Microsoft.Maui.Graphics; using AProgressBar = Android.Widget.ProgressBar; @@ -14,6 +15,18 @@ public static void UpdateProgress(this AProgressBar platformProgressBar, IProgre } public static void UpdateProgressColor(this AProgressBar platformProgressBar, IProgress progress) + { + if (platformProgressBar is LinearProgressIndicator materialProgressBar) + { + materialProgressBar.UpdateLinearProgressIndicatorColor(progress); + } + else + { + platformProgressBar.UpdateProgressBarColor(progress); + } + } + + static void UpdateProgressBarColor(this AProgressBar platformProgressBar, IProgress progress) { Color color = progress.ProgressColor; @@ -27,9 +40,30 @@ public static void UpdateProgressColor(this AProgressBar platformProgressBar, IP var tintList = ColorStateList.ValueOf(color.ToPlatform()); if (platformProgressBar.Indeterminate) + { platformProgressBar.IndeterminateTintList = tintList; + } else + { platformProgressBar.ProgressTintList = tintList; + } + } + } + + static void UpdateLinearProgressIndicatorColor(this LinearProgressIndicator materialProgressBar, IProgress progress) + { + Color color = progress.ProgressColor; + + if (color is null) + { + // Reset to theme default by passing empty array - Material3's setIndicatorColor() + // automatically resolves this to theme's colorPrimary when length == 0 + materialProgressBar.SetIndicatorColor([]); + } + else + { + var colorArray = new int[] { color.ToPlatform() }; + materialProgressBar.SetIndicatorColor(colorArray); } } }