I'm writing native Android code in a Nativescript Angular app, to display a notification with sound.
I followed the recommendations in this answer.
I have a sound file named a.mp3, in the following folder:
Here is the code to configure the sound of the notification:
const uri = new android.net.Uri.Builder()
.scheme(android.content.ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(application.android.nativeApp.getPackageName())
.appendPath("raw")
.appendPath("a.mp3")
.build();
const AudioAttributes = android.media.AudioAttributes;
const audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
Here is the code to display a notification:
const NOTIFICATION_ID = 234;
const CHANNEL_ID = "my_channel_01";
const name = "my notifications";
const Description = "some desc";
const title = "notif title";
const message = "This notification has been triggered by me";
const NotificationManager = android.app.NotificationManager;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
const importance = android.app.NotificationManager.IMPORTANCE_HIGH;
const mChannel = new android.app.NotificationChannel(CHANNEL_ID, name, importance);
mChannel.setSound(uri, audioAttributes);
mChannel.setDescription(Description);
mChannel.enableLights(true);
mChannel.setLightColor(android.graphics.Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern([100, 200, 300, 400, 500, 400, 300, 200, 400]);
mChannel.setShowBadge(false);
notificationManager.createNotificationChannel(mChannel);
}
///////// Create an activity on tap (intent)
const Intent = android.content.Intent;
const PendingIntent = android.app.PendingIntent;
const intent = new Intent(context, com.tns.NativeScriptActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK);
const pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
///////// PRESERVE NAVIGATION ACTIVITY. To start a "regular activity" from your notification, set up
///////// the PendingIntent using TaskStackBuilder so that it creates a new back stack as follows.
///////// SEE: https://developer.android.com/training/notify-user/navigation.html
const TaskStackBuilder = android.support.v4.app.TaskStackBuilder;
const resultIntent = new Intent(context, com.tns.NativeScriptActivity.class);
const stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(com.tns.NativeScriptActivity.class);
stackBuilder.addNextIntent(resultIntent);
///////// Creating a notification
var NotificationCompat = android.support.v4.app.NotificationCompat;
const builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSound(uri)
.setSmallIcon(android.R.drawable.btn_star_big_on)
.setContentTitle(title)
.setContentText(message)
.setStyle(
new NotificationCompat.BigTextStyle()
.bigText("By default, the notification's text content is truncated to fit one line. If you want your notification to be longer, you can enable an expandable notification by adding a style template with setStyle(). For example, the following code creates a larger text area:")
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
// Set the intent that will fire when the user taps the notification
.setContentIntent(pendingIntent)
.setAutoCancel(true);
///////// Show the notification
notificationManager.notify(NOTIFICATION_ID, builder.build());
The notification indeed fires, but the sound file is not played.
Can anyone help to solve this, please?
I also tried using another approach of acquiring the mp3 file, as recommended here:
const uri = android.net.Uri.parse("android.resource://" + context.getPackageName() + "/raw/a.mp3");
But that didn't help either.
Did I put the 'a.mp3' sound file in the correct folder that is recognized by android?
Thanks
I found the solution for this. I'm writing answer so that others can learn from this question.
Regarding the question I asked - Did I put the 'a.mp3' sound file in the correct folder that is recognized by android?
The answer is yes. /App_Resources/Android/src/main/res/raw/ is where the above code will look for the sound file.
But, I needed to do 2 modifications:
A_ The code needs to be changed to not include the file extension:
const uri = new android.net.Uri.Builder()
.scheme(android.content.ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(application.android.nativeApp.getPackageName())
.appendPath("raw")
.appendPath("a") // Referring to a.mp3
.build();
and then the sound uri would be used like in the code in the question:
const mChannel = new android.app.NotificationChannel(CHANNEL_ID, name, importance);
mChannel.setSound(uri, audioAttributes);
or
const builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSound(uri)
B_ I needed to tell webpack to pack the .mp3 files from /App_Resources/Android/src/main/res/raw/ and put it into the nativescript build. To do this, I had to add { from: { glob: "**/*.mp3" } }, to webpack.config.js:
// Copy assets to out dir. Add your own globs as needed.
new CopyWebpackPlugin([
{ from: { glob: "fonts/**" } },
{ from: { glob: "**/*.png" } },
{ from: { glob: "**/*.mp3" } },
], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }),
As webpack isn't configured to copy all files from App_Resources automatically. The same is true for any other file extension that you'd use in your project (for example: .jpg files).
More info here (Q&A on 'nativescript-audio' plugin's git).
Related
I am trying to play custom mp3 sound file of (10-25 seconds) with local notification. I have placed the custom sound file in iOS project folder (same level as Resources) with BundleResource as BuildAction. I have also placed the sound file in Resources folder with same build action. It seems both doesn't work.
var content = new UNMutableNotificationContent
{
Title = title,
//Subtitle = "Notification Subtitle",
Body = body,
Badge = 1,
Sound = UNNotificationSound.GetSound("music.mp3"), //play custom sound
UserInfo = NSDictionary.FromObjectAndKey(NSObject.FromObject(id), NSObject.FromObject(notificationKey))
};
var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(10, false);
var requestID = "request_" + id;
var request = UNNotificationRequest.FromIdentifier(requestID, content, trigger);
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
throw new Exception($"Failed to schedule notification: {err}");
}
});
Any suggestion?
The code works fine, my phone sound was mute.
I am using Xam.Media.Plugin to take pictures. The following is a snippet of my function:
public async Task<string> TakePhoto(string orderNr)
{
...
var mediaOptions = new StoreCameraMediaOptions()
{
CompressionQuality = 25,
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Custom,
CustomPhotoSize = 75,
Directory = $"Order_{orderNr}"
};
var file = await CrossMedia.Current.TakePhotoAsync(mediaOptions);
}
After the order is completed, I want to delete the entire folder with all of the pictures that were saved. How do I retrieve the path of the folder? I want to avoid getting the path after the picture is taken.
on Android,
Environment.DirectoryPictures
on iOS
Environment.SpecialFolder.Personal
from Github
To get the private path var privatePath = file.Path;
To get the public Album path var publicAlbumPath = file.AlbumPath;
Reference: Documentation sample project
I have UWP Windows application, developed under the Xamarin.forms. I have implemented the Toast notifications but I am facing the issue with this. In some Windows 10 systems, it is working and showing the toast notification properly, but in some of the Windows 10 systems (even having the same Windows 10 OS update) it is not working.
Below first code snippets that I have implemented in the Native UWP.
string msg = "Toast Notification Header";
string subMsg = "Toast Notification Title";
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastTextElements = toastXml.GetElementsByTagName("text");
toastTextElements[0].AppendChild(toastXml.CreateTextNode(msg));
toastTextElements[1].AppendChild(toastXml.CreateTextNode(subMsg));
//To play the custom sound
var toastNode = toastXml.SelectSingleNode("/toast");
var audio = toastXml.CreateElement("audio");
audio.SetAttribute("src", "ms-appx:///Assets/incoming_message.wav");
audio.SetAttribute("loop", "false");
toastNode.AppendChild(audio);
var toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier().Show(toast);
Below second code snippets that I have implemented in the Native UWP.
// "With Microsoft.Toolkit.Uwp.Notifications"
// Construct the toast content
ToastContent toastContent = new ToastContent()
{
Visual = new ToastVisual()
{
BindingGeneric = new ToastBindingGeneric()
{
Children =
{
new AdaptiveText()
{
Text = "Toast Notification Header"
},
new AdaptiveText()
{
Text = "Toast Notification Content"
}
}
}
}
};
bool supportsCustomAudio = true;
// If we're running on Desktop before Version 1511, do NOT include custom audio
// since it was not supported until Version 1511, and would result in a silent toast.
if (AnalyticsInfo.VersionInfo.DeviceFamily.Equals("Windows.Desktop")
&& !ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 2))
{
supportsCustomAudio = false;
}
if (supportsCustomAudio)
{
toastContent.Audio = new ToastAudio()
{
Src = new Uri("ms-appx:///Assets/incoming_message.wav")
};
}
// And create the toast notification
ToastNotification notification = new ToastNotification(toastContent.GetXml());
// And then send the toast
ToastNotificationManager.CreateToastNotifier().Show(notification);
Above code snips showing the Toast notification in some Windows 10 system and not working in some other Windows 10 system.
Kindly guide me on this. Thanks in advance.
Regards,
Vivek
Please follow these steps to add toast notification in UWP Project.
Step 1:- Create a new UWP project.
Step 2:- Go to the code-behind and add the namespace.
using Windows.UI.Notifications;
using
NotificationsExtensions.Toasts;
Step 3:- I created a Toast Generic Template like the following code:
public static Windows.Data.Xml.Dom.XmlDocument CreateToast()
{
var xDoc = new XDocument(
new XElement("toast",
new XElement("visual",
new XElement("binding", new XAttribute("template", "ToastGeneric"),
new XElement("text", "C# Corner"),
new XElement("text", "Do you got MVP award?")
)
),// actions
new XElement("actions",
new XElement("action", new XAttribute("activationType", "background"),
new XAttribute("content", "Yes"), new XAttribute("arguments", "yes")),
new XElement("action", new XAttribute("activationType", "background"),
new XAttribute("content", "No"), new XAttribute("arguments", "no"))
)
)
);
var xmlDoc = new Windows.Data.Xml.Dom.XmlDocument();
xmlDoc.LoadXml(xDoc.ToString());
return xmlDoc;
}
Step 4:- Create a toast notification object using XML document.
var xmdock = CreateToast();
var toast = new ToastNotification(xmdock);
Next show the toast using ToastNotificationManager class.
var notifi = Windows.UI.Notifications.ToastNotificationManager.CreateToastNotifier();
notifi.Show(toast);
Step 5:- C# code-behind:
private void showToastBtn_Click(object sender, RoutedEventArgs e)
{
var xmdock = CreateToast();
var toast = new ToastNotification(xmdock);
var notifi = Windows.UI.Notifications.ToastNotificationManager.CreateToastNotifier();
notifi.Show(toast);
}
I hope the above code will be useful for you.
Thank you
I have this code for downloading a file which runs correct:
var base64EncodedBytes = System.Convert.FromBase64String(item.FileDataAsBase64String);
var downloadDirectory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePath = Path.Combine(downloadDirectory, "test.pdf");
var streamWriter = File.Create(filePath);
streamWriter.Close();
File.WriteAllBytes(filePath, base64EncodedBytes);
I'm able to locate the file I downloaded in the Downloads folder, but also I want to show a notification in the notification bar that the file has been downloaded and with a click in the notifications the user to be able to open the downloaded file.
Is that possible?
You can use Plugin.LocalNotification to show the notification once the file is downloaded.
try
{
var base64EncodedBytes = System.Convert.FromBase64String(item.FileDataAsBase64String);
var downloadDirectory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePath = Path.Combine(downloadDirectory, "test.pdf");
var streamWriter = File.Create(filePath);
streamWriter.Close();
File.WriteAllBytes(filePath, base64EncodedBytes);
DisplayNotification("test.pdf downloaded successfully", filePath);
}
catch(System.Exception e)
{
System.Console.WriteLine(e.ToString());
DisplayNotification("Download Failed",string.Empty);
}
public void DisplayNotification(string message, string filePath)
{
var notification = new NotificationRequest
{
NotificationId = 100,
Title = "Your App name",
Description = message,
ReturningData = filePath, // Returning data when tapped on notification.
NotifyTime = DateTime.Now.AddSeconds(30) // Used for Scheduling local notification, if not specified notification will show immediately.
};
NotificationCenter.Current.Show(notification);
}
Note: Make sure to initialize the plugin setup in both the project iOS and Android.
On the App.xaml.cs I have the following code
private async void OnCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs e)
{
var loader = ResourceLoader.GetForCurrentView();
var generalCommand = new SettingsCommand("General Settings", "General Settings", handler =>
{
var generalSettings = new GeneralSettingsFlyout();
generalSettings.Show();
});
e.Request.ApplicationCommands.Add(generalCommand);
object data;
IAuthService _authService = new AuthService();
if (Global.UserId == 0)
data = await _authService.GetSettingValueBySettingName(DatabaseType.GeneralDb, ApplicationConstants.GeneralDbSettingNames.ShowSupportInfo);
else
data = await _authService.GetSettingValueBySettingName(DatabaseType.UserDb, ApplicationConstants.UserDbSettingNames.ShowSupportInfo);
if (data != null && data.ToString().Equals("1"))
{
var supportCommand = new SettingsCommand("Support", "Support", handler =>
{
var supportPane = new SupportFlyout();
supportPane.Show();
});
e.Request.ApplicationCommands.Add(supportCommand);
}
var aboutCommand = new SettingsCommand("About", loader.GetString("Settings_OptionLabels_About"), handler =>
{
var aboutPane = new About();
aboutPane.Show();
});
e.Request.ApplicationCommands.Add(aboutCommand);
}
This code adds the setting "General Settings" but neither "Support" or "About" commands. Can anyone advice what's wrong with this code?
Instead of querying the commands from your service when they are requested you'll need to query them ahead of time and then add the already known commands.
You cannot use await in OnCommandsRequested.
A method returns when it gets to the first await, so only commands added to the request before the await will be used.
Since the SettingsPaneCommandsRequestedEventArgs doesn't provide a deferral there is no way to tell the requester to wait for internal async calls to complete.
Note also that SettingsPane is deprecated and not recommended for new app development for Windows 10.