Skip to content

Commit

Permalink
BadgeCounter #807 (#1126)
Browse files Browse the repository at this point in the history
* BadgeCounter #807

* Rename to Badge

* Update tests

* Remove Xamarin.Android.ShortcutBadger dependency

* Fix Tizen build

* Add default badge provider, add nova

* Fix SamsungBadgeProvider

* SetProvider

* Initialize array only once

* `dotnet format`

* Update SamsungBadgeProvider.cs

* Update BadgeViewModel.cs

* Update DefaultBadgeProvider.android.cs

* Use `Lazy<T>` to ensure thread safety

* GetCount

- Uncommented lines in the csproj file to address Android App installation failure.

Toggle Home Screen Icon Badge Count

- Added column and row spacing to improve layout in the BadgePage.xaml file.

SetBadgeProvider method update

- Renamed SetProvider method to SetBadgeProvider in the BadgeFactory.android.cs file.

GetCount method implementation

- Implemented GetCount method in the BadgeImplementation.android.cs, DefaultBadgeProvider.android.cs, BadgeImplementation.macios.cs, and BadgeImplementation.tizen.cs files.

Added GetCount method to IBadge interface

- Added GetCount method to the IBadge.shared.cs file.

* feat: Add support for Samsung badge provider

This commit adds support for the Samsung badge provider in the Android platform. It includes changes to the `SamsungBadgeProvider` class and the `DefaultBadgeProvider.android.cs` file.

- Added a new static readonly string array `packageNameArray` in the `SamsungBadgeProvider` class.
- Modified the `SetCount` method in the `SamsungBadgeProvider` class to use `packageNameArray` instead of directly passing a string array.
- Added a new method `GetCount()` in the `SamsungBadgeProvider` class.
- Renamed the method `IsSupported()` to `CanSetBadgeCounter()` in the `DefaultBadgeProvider.android.cs` file.
- Added debug log statements for error handling scenarios in both classes.

* Fix SamsungBadgeProvider.cs and BadgeTests.cs

- Fix a typo in SamsungBadgeProvider.cs by changing "new[1]" to "new string[1]"
- Add a new test method GetBadgeFailsOnNet() in BadgeTests.cs that tests the behavior of getting badge count on .NET platform
- Modify the existing test method SetBadgeFailsOnNet() in BadgeTests.cs to throw NotSupportedException instead of NotImplementedException
- Add a new method GetCount() in BadgeImplementationMock.cs that throws NotImplementedException

* Fix build error

Sorry Vlad - this was my fault!

* Update CommunityToolkit.Maui.Sample.csproj

* Update DefaultBadgeProvider.android.cs

* Update BadgeImplementation.macios.cs

* Update EssentialsGalleryViewModel.cs

* Update BaseGalleryViewModel.cs

* Update MainApplication.cs

* Add null check

* Remove `count` field

* Update DefaultBadgeProvider.android.cs

* Add additional text explaining Bade

* Remove GetCount, use uint instead of int

* fix tizen build

* Update src/CommunityToolkit.Maui.Core/Essentials/Badge/BadgeImplementation.windows.cs

Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>

* Update src/CommunityToolkit.Maui.Core/Essentials/Badge/BadgeImplementation.tizen.cs

Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>

* Update src/CommunityToolkit.Maui.Core/Essentials/Badge/Android/BadgeFactory.android.cs

Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>

* Update src/CommunityToolkit.Maui.Core/Essentials/Badge/Android/DefaultBadgeProvider.android.cs

Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>

---------

Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com>
Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>
  • Loading branch information
3 people committed Jul 23, 2023
1 parent a4e6801 commit b70a32d
Show file tree
Hide file tree
Showing 25 changed files with 512 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,4 @@ paket-files/

# Visual Studio Code
.vscode
/samples/workload-install.ps1
1 change: 1 addition & 0 deletions samples/CommunityToolkit.Maui.Sample/AppShell.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public partial class AppShell : Shell
CreateViewModelMapping<VariableMultiValueConverterPage, VariableMultiValueConverterViewModel, ConvertersGalleryPage, ConvertersGalleryViewModel>(),

// Add Essentials View Models
CreateViewModelMapping<BadgePage, BadgeViewModel, EssentialsGalleryPage, EssentialsGalleryViewModel>(),
CreateViewModelMapping<FileSaverPage, FileSaverViewModel, EssentialsGalleryPage, EssentialsGalleryViewModel>(),
CreateViewModelMapping<FolderPickerPage, FolderPickerViewModel, EssentialsGalleryPage, EssentialsGalleryViewModel>(),
CreateViewModelMapping<SpeechToTextPage, SpeechToTextViewModel, EssentialsGalleryPage, EssentialsGalleryViewModel>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
<ApplicationVersion>1</ApplicationVersion>

<!--
Uncomment these lines if you need to debug the SG code
Uncomment the line below if you need to debug the SG code
If you see any LongPath issue on Windows, check this doc
https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later
-->
<!--<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>-->

<!-- Uncomment the line below if Android App installation fails -->
<!--<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>-->
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -79,4 +82,5 @@
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))=='false' and $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))=='maccatalyst' and $(Configuration) == 'Debug'">
<RuntimeIdentifiers>maccatalyst-arm64;maccatalyst-x64</RuntimeIdentifiers>
</PropertyGroup>

</Project>
3 changes: 3 additions & 0 deletions samples/CommunityToolkit.Maui.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using CommunityToolkit.Maui.ApplicationModel;
using CommunityToolkit.Maui.Maps;
using CommunityToolkit.Maui.Markup;
using CommunityToolkit.Maui.Media;
Expand Down Expand Up @@ -156,6 +157,7 @@ static void RegisterViewsAndViewModels(in IServiceCollection services)
services.AddTransientWithShellRoute<VariableMultiValueConverterPage, VariableMultiValueConverterViewModel>();

// Add Essentials Pages + ViewModels
services.AddTransientWithShellRoute<BadgePage, BadgeViewModel>();
services.AddTransientWithShellRoute<FileSaverPage, FileSaverViewModel>();
services.AddTransientWithShellRoute<FolderPickerPage, FolderPickerViewModel>();
services.AddTransientWithShellRoute<SpeechToTextPage, SpeechToTextViewModel>();
Expand Down Expand Up @@ -199,6 +201,7 @@ static void RegisterEssentials(in IServiceCollection services)
services.AddSingleton<IDeviceInfo>(DeviceInfo.Current);
services.AddSingleton<IFileSaver>(FileSaver.Default);
services.AddSingleton<IFolderPicker>(FolderPicker.Default);
services.AddSingleton<IBadge>(Badge.Default);
services.AddSingleton<ISpeechToText>(SpeechToText.Default);
services.AddSingleton<ITextToSpeech>(TextToSpeech.Default);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
x:Class="CommunityToolkit.Maui.Sample.Pages.Essentials.BadgePage"
xmlns:vm="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Essentials"
x:TypeArguments="vm:BadgeViewModel"
x:DataType="vm:BadgeViewModel"
Title="Badge">

<Grid
RowDefinitions="40,60,*,*,80"
ColumnDefinitions="*,*"
ColumnSpacing="12"
RowSpacing="12">

<Label
Grid.Row="0"
Grid.ColumnSpan="2"
HorizontalTextAlignment="Center"
Text="Badge allows developers to set the app icon badge number on the homescreen" />

<Label
Grid.Row="1"
Grid.ColumnSpan="2"
HorizontalTextAlignment="Center"
Text="Toggle the app icon Badge count using the buttons below, then minimize the application to see its Badge count updated on the home screen" />

<Button
Text="+"
Grid.Column="0"
Grid.Row="2"
SemanticProperties.Hint="Increments AppIcon badge"
Command="{Binding IncrementCommand}"
VerticalOptions="End"
HorizontalOptions="End" />

<Button
Text="-"
Grid.Column="1"
Grid.Row="2"
SemanticProperties.Hint="Decrements AppIcon badge"
Command="{Binding DecrementCommand}"
VerticalOptions="End"
HorizontalOptions="Start" />

<Label
Grid.Row="3"
Grid.ColumnSpan="2"
VerticalOptions="Start"
VerticalTextAlignment="Start"
Text="Toggle Home Screen Icon Badge Count"
FontSize="18"
HorizontalOptions="Center" />

<Label
BackgroundColor="LightYellow"
Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalTextAlignment="Center"
FontAttributes="Bold"
Text="Note: Android requires device-specific implementation to support app icon badge counts. See the .NET MAUI Community Toolkit documentation for more information" />

</Grid>

</pages:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CommunityToolkit.Maui.Sample.ViewModels.Essentials;

namespace CommunityToolkit.Maui.Sample.Pages.Essentials;

public partial class BadgePage : BasePage<BadgeViewModel>
{
public BadgePage(BadgeViewModel viewModel) : base(viewModel)
{
InitializeComponent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Samsung -->
<uses-permission android:name="com.sec.android.provider.badge.permission.READ" />
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE" />

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<queries>
<intent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Android.App;
using Android.Runtime;
using CommunityToolkit.Maui.ApplicationModel;

namespace CommunityToolkit.Maui.Sample;

Expand All @@ -9,6 +10,9 @@ public class MainApplication : MauiApplication
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
var samsungProvider = new SamsungBadgeProvider();
BadgeFactory.SetBadgeProvider("com.sec.android.app.launcher", samsungProvider);
BadgeFactory.SetBadgeProvider("com.sec.android.app.twlauncher", samsungProvider);
}

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Android.Content;
using Android.Database;
using CommunityToolkit.Maui.ApplicationModel;
using Application = Android.App.Application;

namespace CommunityToolkit.Maui.Sample;

class SamsungBadgeProvider : IBadgeProvider
{
const string contentStringUri = "content://com.sec.badge/apps?notify=true";
static readonly string[] contentProjection = { "_id", "class" };
static readonly string[] packageNameArray = new string[1];

public void SetCount(uint count)
{
var contentUri = Android.Net.Uri.Parse(contentStringUri);
if (contentUri is null)
{
return;
}

var packageName = Application.Context.PackageName;
if (packageName is null)
{
return;
}

var componentName = Application.Context.PackageManager?.GetLaunchIntentForPackage(packageName)?.Component;
if (componentName is null)
{
return;
}

var contentResolver = Application.Context.ContentResolver;
ICursor? cursor = null;
try
{
packageNameArray[0] = packageName;
cursor = contentResolver?.Query(contentUri, contentProjection, "package=?", packageNameArray, null);
if (cursor is not null)
{
var entryActivityExist = false;
var selectionArgs = new string[1];
while (cursor.MoveToNext())
{
var id = cursor.GetInt(0);
selectionArgs[0] = id.ToString();
var contentValues = GetContentValues(componentName, count, false);
contentResolver?.Update(contentUri, contentValues, "_id=?", selectionArgs);
if (componentName.ClassName.Equals(cursor.GetString(cursor.GetColumnIndex("class"))))
{
entryActivityExist = true;
}
}

if (!entryActivityExist)
{
var contentValues = GetContentValues(componentName, count, true);
contentResolver?.Insert(contentUri, contentValues);
}
}
}
finally
{
if (cursor?.IsClosed is false)
{
cursor.Close();
}
}
}

static ContentValues GetContentValues(ComponentName componentName, uint badgeCount, bool isInsert)
{
var contentValues = new ContentValues();
if (isInsert)
{
contentValues.Put("package", componentName.PackageName);
contentValues.Put("class", componentName.ClassName);
}

contentValues.Put("badgecount", badgeCount);
return contentValues;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ protected BaseGalleryViewModel(SectionModel[] items)
{
if (DoesItemsArrayContainDuplicates(items, out var duplicatedSectionModels))
{
throw new InvalidOperationException($"Duplicate {nameof(SectionModel)}.{nameof(SectionModel.ViewModelType)} found for {duplicatedSectionModels.First().ViewModelType}");
throw new InvalidOperationException($"Duplicate {nameof(SectionModel)}.{nameof(SectionModel.ViewModelType)} found for {duplicatedSectionModels[0].ViewModelType}");
}

Items = items.OrderBy(x => x.Title).ToList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using CommunityToolkit.Maui.ApplicationModel;
using CommunityToolkit.Mvvm.Input;

namespace CommunityToolkit.Maui.Sample.ViewModels.Essentials;

public partial class BadgeViewModel : BaseViewModel
{
readonly IBadge badge;

uint count;

public BadgeViewModel(IBadge badge)
{
this.badge = badge;
}

[RelayCommand]
void Increment()
{
count++;
badge.SetCount(count);
}

[RelayCommand]
void Decrement()
{
if (count > 0)
{
count--;
badge.SetCount(count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class EssentialsGalleryViewModel : BaseGalleryViewModel
public EssentialsGalleryViewModel()
: base(new[]
{
SectionModel.Create<BadgeViewModel>("Badge", "Allows the user to set app icon badge count on the home screen"),
SectionModel.Create<FileSaverViewModel>("FileSaver", "Allows the user to save files to the filesystem"),
SectionModel.Create<FolderPickerViewModel>("FolderPicker", "Allows picking folders from the file system"),
SectionModel.Create<SpeechToTextViewModel>("SpeechToText", "Converts speech to text"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@

<PackageReference Include="System.Speech" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0-windows10.0.19041.0'" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Android.Content;
using Android.Content.PM;

namespace CommunityToolkit.Maui.ApplicationModel;

/// <summary>
/// Factory for <see cref="IBadge"/>
/// </summary>
public static class BadgeFactory
{
static readonly Dictionary<string, IBadgeProvider> providers = new();
static readonly DefaultBadgeProvider defaultBadgeProvider = new();

/// <summary>
/// Register provider for launcher type
/// </summary>
/// <param name="launcherType">Launcher type</param>
/// <param name="provider">Provider implementation</param>
public static void SetBadgeProvider(string launcherType, IBadgeProvider provider)
{
providers[launcherType] = provider;
}

/// <summary>
/// Get badge provider for current launcher
/// </summary>
/// <returns>Provider implementation</returns>
public static IBadgeProvider GetBadgeProvider()
{
var launcherType = GetLauncherType();
if (launcherType is null)
{
return defaultBadgeProvider;
}

providers.TryGetValue(launcherType, out var provider);
return provider ?? defaultBadgeProvider;
}

static string? GetLauncherType()
{
using var intent = new Intent(Intent.ActionMain);
intent.AddCategory(Intent.CategoryHome);
using var resolveInfo = OperatingSystem.IsAndroidVersionAtLeast(33) ?
Application.Context.PackageManager?.ResolveActivity(intent, PackageManager.ResolveInfoFlags.Of(0)) :
Application.Context.PackageManager?.ResolveActivity(intent, PackageInfoFlags.MatchDefaultOnly);

if (resolveInfo is { ActivityInfo.PackageName: not null })
{
return resolveInfo.ActivityInfo.PackageName;
}

return Application.Context.PackageName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace CommunityToolkit.Maui.ApplicationModel;

/// <inheritdoc />
public class BadgeImplementation : IBadge
{
/// <inheritdoc />
public void SetCount(uint count)
{
var badgeProvider = BadgeFactory.GetBadgeProvider();
badgeProvider.SetCount(count);
}
}

0 comments on commit b70a32d

Please sign in to comment.