Skip to content

Commit

Permalink
Port Local Notification Sample to .NET 7 (#341)
Browse files Browse the repository at this point in the history
* New Button Sample port

* Made a few changes on MainActivity.cs

* Removed autogenerated files and a modification on MainActivity.cs

* Added Metadata.xml file

* Added string interpolation on MainActivity.cs and updated the screenshot

* Added string interpolation

* New Local Notifications sample port from legacy

* Modified a comment and removed an extra line on MainActivity.cs

* Added spacing at line 76 on MainActivity.cs

* Added additional null check on SecondActivity.cs

* Addded PendingIntentFlags.Immutable for API 31+ when creating PendingIntent

* Delete Metadata.xml

* Update to latest Xamarin.AndroidX.AppCompat

* Better use of C# features/null check

* Fix project namespaces

* Request appropriate permissions

* Update to .NET 7

Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
  • Loading branch information
officialgio and jonathanpeppers authored Dec 19, 2022
1 parent 7ea1e34 commit 7587222
Show file tree
Hide file tree
Showing 33 changed files with 330 additions and 0 deletions.
27 changes: 27 additions & 0 deletions LocalNotifications/LocalNotifications.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32519.111
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalNotifications", "LocalNotifications\LocalNotifications.csproj", "{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Release|Any CPU.Build.0 = Release|Any CPU
{4B2A0076-A973-4AC3-B762-4B889BEFC9C2}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5AFA1196-F555-4E40-8873-088B8649B8A4}
EndGlobalSection
EndGlobal
7 changes: 7 additions & 0 deletions LocalNotifications/LocalNotifications/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:theme="@style/AppTheme" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true">
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
</manifest>
15 changes: 15 additions & 0 deletions LocalNotifications/LocalNotifications/LocalNotifications.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-android</TargetFramework>
<SupportedOSPlatformVersion>23</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationId>com.companyname.LocalNotifications</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" />
</ItemGroup>
</Project>
130 changes: 130 additions & 0 deletions LocalNotifications/LocalNotifications/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.Runtime;
using AndroidX.AppCompat.App;
using AndroidX.Core.App;
using Java.Lang;
using TaskStackBuilder = AndroidX.Core.App.TaskStackBuilder;

namespace LocalNotifications;

[Activity (Label = "Notifications", MainLauncher = true, Icon = "@drawable/Icon")]
public class MainActivity : AppCompatActivity
{
const string PERMISSION = "android.permission.POST_NOTIFICATIONS";
const int REQUEST_CODE = 1;
/// <summary>
/// Unique ID for our notification
/// </summary>
const int NOTIFICATION_ID = 1000;
const string CHANNEL_ID = "location_notification";
public const string COUNT_KEY = "count";

// Number of times the button is tapped (starts with first tap):
int count = 1;

protected override void OnCreate (Bundle? bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);

// Display the "Hello World, Click Me!" button and register its event handler:
var button = RequireViewById<Button> (Resource.Id.MyButton);
button.Click += (sender, e) =>
{
if (CheckSelfPermission(PERMISSION) == Permission.Denied)
{
ActivityCompat.RequestPermissions(this, new[] { PERMISSION }, REQUEST_CODE);
}
else
{
CreateNotification();
}
};

CreateNotificationChannel();
}

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if (requestCode == REQUEST_CODE)
{
if (permissions.Length > 0 && permissions[0] == PERMISSION &&
grantResults.Length > 0 && grantResults[0] == Permission.Granted)
{
CreateNotification();
}
else
{
Toast.MakeText(this, "Unable to request permissions!", ToastLength.Long)!.Show();
}
}
}

void CreateNotification()
{
// Pass the current button press count value to the next activity:
var valuesForActivity = new Bundle();
valuesForActivity.PutInt(COUNT_KEY, count);

// When the user clicks the notification, SecondActivity will start up.
var resultIntent = new Intent(this, typeof(SecondActivity));

// Pass some values to SecondActivity:
resultIntent.PutExtras(valuesForActivity);

// Construct a back stack for cross-task navigation:
var stackBuilder = TaskStackBuilder.Create(this);
ArgumentNullException.ThrowIfNull(stackBuilder);
stackBuilder.AddParentStack(Class.FromType(typeof(SecondActivity)));
stackBuilder.AddNextIntent(resultIntent);

// Create the PendingIntent with the back stack:
var resultPendingIntent = stackBuilder.GetPendingIntent(0, (int)PendingIntentFlags.Immutable);

// Build the notification:
var builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetAutoCancel(true) // Dismiss the notification from the notification area when the user clicks on it
.SetContentIntent(resultPendingIntent) // Start up this activity when the user clicks the intent.
.SetContentTitle("Button Clicked") // Set the title
.SetNumber(count) // Display the count in the Content Info
.SetSmallIcon(Resource.Mipmap.ic_stat_button_click) // This is the icon to display
.SetContentText($"The button has been clicked {count} times."); // the message to display.

// Finally, publish the notification:
var notificationManager = NotificationManagerCompat.From(this);
if (notificationManager.AreNotificationsEnabled())
{
notificationManager.Notify(NOTIFICATION_ID, builder.Build());
}
else
{
Toast.MakeText(this, "Notifications are not enabled!", ToastLength.Long)!.Show();
}

// Increment the button press count:
count++;
}

void CreateNotificationChannel ()
{
ArgumentNullException.ThrowIfNull (Resources);

// Creating a NotificationChannel is only needed in API 26+
if (OperatingSystem.IsAndroidVersionAtLeast (26))
{
var name = Resources.GetString (Resource.String.channel_name);
var description = GetString (Resource.String.channel_description);
var channel = new NotificationChannel (CHANNEL_ID, name, NotificationImportance.Default)
{
Description = description
};

if (GetSystemService(NotificationService) is NotificationManager manager)
{
manager.CreateNotificationChannel(channel);
}
}
}
}
44 changes: 44 additions & 0 deletions LocalNotifications/LocalNotifications/Resources/AboutResources.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.

For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:

Resources/
drawable/
icon.png

layout/
main.xml

values/
strings.xml

In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "Resource"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the Resource class would expose:

public class Resource {
public class Drawable {
public const int icon = 0x123;
}

public class Layout {
public const int main = 0x456;
}

public class Strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}

You would then use Resource.Drawable.icon to reference the drawable/icon.png file, or
Resource.Layout.main to reference the layout/main.xml file, or Resource.Strings.first_string
to reference the first string in the dictionary file values/strings.xml.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions LocalNotifications/LocalNotifications/Resources/layout/Main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/MyButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/Hello" />
</LinearLayout>
14 changes: 14 additions & 0 deletions LocalNotifications/LocalNotifications/Resources/layout/Second.axml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px">
<TextView
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/textView1" />
</LinearLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>

<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<resources>
<string name="app_name">LocalNotifications</string>
<string name="Hello">Hello World, Click Me!</string>

<string name="channel_name">Local Notifications</string>
<string name="channel_description">The count from MainActivity.</string>
</resources>
13 changes: 13 additions & 0 deletions LocalNotifications/LocalNotifications/Resources/values/styles.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>
24 changes: 24 additions & 0 deletions LocalNotifications/LocalNotifications/SecondActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using AndroidX.AppCompat.App;

namespace LocalNotifications;

[Activity (Label = "Second Activity")]
public class SecondActivity : AppCompatActivity
{
protected override void OnCreate (Bundle? bundle)
{
base.OnCreate (bundle);

// Get the count value passed to us from MainActivity:
var count = Intent?.Extras?.GetInt (MainActivity.COUNT_KEY, -1);

// No count was passed? Then just return.
if (count is not null && count <= 0)
return;

// Display the count sent from the first activity:
SetContentView (Resource.Layout.Second);
var txtView = RequireViewById<TextView> (Resource.Id.textView1);
txtView.Text = $"You clicked the button {count} times in the previous activity.";
}
}
20 changes: 20 additions & 0 deletions LocalNotifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Xamarin.Android - Android Local Notifications Sample
description: This sample app accompanies the article Using Local Notifications in Xamarin.Android.
page_type: sample
languages:
- csharp
products:
- xamarin
urlFragment: localnotifications
---
# Android Local Notifications Sample

This sample app accompanies the article,
[Walkthrough - Using Local Notifications in Xamarin.Android](https://docs.microsoft.com/xamarin/android/app-fundamentals/notifications/local-notifications-walkthrough).

![Android app screenshot](Screenshots/screenshot-1.png)

When you tap the button displayed in the MainActivity screen, a
notification is created. When you tap the notification, it
takes you to a SecondActivity screen.
Binary file added LocalNotifications/Screenshots/screenshot-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added LocalNotifications/Screenshots/screenshot-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7587222

Please sign in to comment.