Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions sample/Maui.Android.InAppUpdates.SampleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.5" />
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.82" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.7" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>

<PropertyGroup Label="NuGet">
<PackageId>Oscore.$(AssemblyName)</PackageId>
<Description>NuGet package that implementing Android In-App Updates for MAUI with debugging capabilities.</Description>
<PackageTags>maui;android;in-app-updates;updates;in-app;net8;dotnet;csharp</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net9.0-android'" Label="Android In-app Updates">
<PackageReference Include="Xamarin.AndroidX.Activity.Ktx" Version="1.10.1.1" />
<PackageReference Include="Xamarin.AndroidX.Collection.Ktx" Version="1.5.0.1" />
<PackageReference Include="Xamarin.AndroidX.Fragment.Ktx" Version="1.8.6.1" />
<PackageReference Include="Xamarin.Google.Android.Play.App.Update.Ktx" Version="2.1.0.14" />
<PackageReference Include="Xamarin.AndroidX.Activity.Ktx" Version="1.10.1.2" />
<PackageReference Include="Xamarin.AndroidX.Collection.Ktx" Version="1.5.0.2" />
<PackageReference Include="Xamarin.AndroidX.Fragment.Ktx" Version="1.8.8" />
<PackageReference Include="Xamarin.Google.Android.Play.App.Update.Ktx" Version="2.1.0.15" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('Xamarin.iOS')) != true AND $(TargetFramework.StartsWith('net9.0-ios')) != true AND $(TargetFramework.StartsWith('net9.0-maccatalyst')) != true ">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,30 @@ public static class DefaultUserInterface
/// Displays a short duration toast message at the center of the screen.
/// </summary>
/// <param name="text">The text to be displayed in the toast message.</param>
public static void ShowShortToast(
string text)
public static void ShowShortToast(string text)
{
Toast.MakeText(
context: Platform.AppContext,
text: text,
duration: ToastLength.Short)?.Show();
try
{
if (Platform.AppContext is null)
{
Handler.Options.DebugAction($"Cannot show toast - Platform.AppContext is null: {text}");
return;
}

Toast.MakeText(
context: Platform.AppContext,
text: text,
duration: ToastLength.Short)?.Show();
}
catch (Exception ex)
{
Handler.Options.DebugAction($"Failed to show toast '{text}': {ex}");
}
}

/// <summary>
/// Displays a snackbar with an action to complete the app update process.
/// If snackbar fails due to theme incompatibility, falls back to toast message.
/// </summary>
/// <param name="text">The text to display on the snackbar.</param>
/// <param name="actionText">The text for the action button.</param>
Expand All @@ -30,18 +43,86 @@ public static void ShowSnackbar(
string actionText,
Action<global::Android.Views.View> clickHandler)
{
if (Platform.CurrentActivity?.Window?.DecorView is not {} view ||
Snackbar.Make(
text: text,
duration: BaseTransientBottomBar.LengthIndefinite,
view: view) is not {} snackbar)
var fallbackMessage = $"{text} - {actionText}";

try
{
var view = Platform.CurrentActivity?.Window?.DecorView;
if (view is null)
{
FallbackToToastWithAction("Cannot show snackbar - no active view", fallbackMessage, clickHandler, null);
return;
}

var snackbar = Snackbar.Make(view, text, BaseTransientBottomBar.LengthIndefinite);
if (snackbar is null)
{
FallbackToToastWithAction("Cannot create snackbar", fallbackMessage, clickHandler, view);
return;
}

snackbar.SetAction(text: actionText, clickHandler: clickHandler);
snackbar.Show();
}
catch (Exception ex) when (IsThemeRelated(ex))
{
HandleSnackbarFailure(ex, "theme related", fallbackMessage, clickHandler, Platform.CurrentActivity?.Window?.DecorView);
}
catch (Exception ex)
{
return;
HandleSnackbarFailure(ex, "general exception", fallbackMessage, null, null);
}

snackbar.SetAction(
text: actionText,
clickHandler: clickHandler);
snackbar.Show();
}

private static void FallbackToToastWithAction(
string debugMessage,
string fallbackMessage,
Action<global::Android.Views.View>? clickHandler,
global::Android.Views.View? fallbackView)
{
Handler.Options.DebugAction($"{debugMessage}, falling back to toast and auto-triggering action");
ShowShortToast(fallbackMessage);

// Auto-trigger the action since user can't click the snackbar
if (clickHandler is not null && fallbackView is not null)
{
try
{
clickHandler(fallbackView);
}
catch (Exception actionEx)
{
Handler.Options.DebugAction($"Error auto-executing snackbar action: {actionEx}");
}
Comment thread
HavenDV marked this conversation as resolved.
}
}

private static void HandleSnackbarFailure(
Exception ex,
string errorType,
string fallbackMessage,
Action<global::Android.Views.View>? clickHandler,
global::Android.Views.View? view)
{
Handler.Options.DebugAction($"Snackbar {errorType}: {ex}");
ShowShortToast(fallbackMessage);

// Only auto-trigger for theme-related exceptions where user interaction was expected
if (clickHandler is not null && view is not null)
Comment thread
HavenDV marked this conversation as resolved.
{
try
{
clickHandler(view);
}
catch (Exception actionEx)
{
Handler.Options.DebugAction($"Error executing snackbar action: {actionEx}");
}
}
}

private static bool IsThemeRelated(Exception ex) =>
ex is global::Android.Views.InflateException ||
(ex is Java.Lang.UnsupportedOperationException &&
ex.Message?.Contains("Failed to resolve attribute", StringComparison.Ordinal) == true);
Comment thread
HavenDV marked this conversation as resolved.
}