Urban Airship recommends creating a custom notification with CustomPushNotificationBuilder if you want to make any modifications to the status bar notification, including trivially changing the icon.
Unfortunately, using a RemoteView for notifications carries many unwanted implications with it related to custom manufacturer and/or platform-specific skins, including text colors and references to private resources (for instance #*android:drawable/notify_panel_notification_icon_bg_tile on Honeycomb/ICS).
There must be a simple way to swap the icon without using RemoteView. How?
I found that by overriding BasicPushNotificationBuilder, I can set the icon quite trivially:
BasicPushNotificationBuilder nb = new BasicPushNotificationBuilder() {
#Override
public Notification buildNotification(String alert,
Map<String, String> extras) {
Notification notification = super.buildNotification(alert,
extras);
// The icon displayed in the status bar
notification.icon = R.drawable.notification;
// The icon displayed within the notification content
notification.contentView.setImageViewResource(
android.R.id.icon, R.drawable.notification);
return notification;
}
};
// Set the custom notification builder
PushManager.shared().setNotificationBuilder(nb);
I know this is an old question, but UrbanAirship get's updated quite often, so I decided to help others who might reach this page. As of version 6.0.1 there's no BasicNotificationBuilder no more. In order to customize your notification with icon and color and whatnot, you need to extend the NotifcationFactory class, and override the createNotification method.
Like shown in the example below:
public class MyNotificationFactory extends NotificationFactory {
public MyNotificationFactory(Context context){
super(context);
}
#Override
public Notification createNotification(PushMessage pushMessage, int i) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext())
.setContentTitle(getContext().getResources().getString(R.string.app_name))
.setContentText(pushMessage.getAlert())
.setSmallIcon(R.drawable.your_icon_here)
.setColor(getContext().getResources().getColor(R.color.your_color_here))
.setAutoCancel(true);
return builder.build();
}
#Override
public int getNextId(PushMessage pushMessage) {
return NotificationIDGenerator.nextID();
}
}
At last you must set this as UrbanAirship's new notification factory in your application class or wherever you initialized UA:
UAirship.shared().getPushManager().setNotificationFactory(new MyNotificationFactory(getApplicationContext()));
We provide a lot of functionality in our default notification factory such as big styles (inbox, text, image), lollipop features (privacy, priority), and interactive notification buttons. If you are only trying to set the icon and maybe the accent color, I recommend the following:
UAirship.takeOff(this, new UAirship.OnReadyCallback() {
#Override
public void onAirshipReady(UAirship airship) {
// Perform any airship configurations here
// Create a customized default notification factory
DefaultNotificationFactory defaultNotificationFactory = new DefaultNotificationFactory(getApplicationContext());
defaultNotificationFactory.setSmallIconId(R.drawable.ic_notification);
defaultNotificationFactory.setColor(NotificationCompat.COLOR_DEFAULT);
// Set it
airship.getPushManager().setNotificationFactory(defaultNotificationFactory);
}
});
Full docs on the class can be found here - http://docs.urbanairship.com/reference/libraries/android/latest/reference/com/urbanairship/push/notifications/DefaultNotificationFactory.html
Related
I am so frustrated with the Xamarin documentations. I am looking at doing one of the most basic thing, which is:
When a local notification comes out, a user taps the notification. It launches the App.
How do I handle this so that the app launches and acts according to the notification?
This is the official Xamarin Local Notifications documentation....
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
In Stack Overflow or google searches I can't find any Solutions.
Nothing is mentioned on how to act on a tap to the notification
Ah yes, the docs can be improved! They just assume that you understand it, you should add a pull request or an issue to improve that page!
After you have created:
The INotificationManager interface in the Core project,
The iOS platform changes- iOSNotificationManager, iOSNotificationReceiver, AppDelegate changes
The Android platform changes- AndroidNotificationManager, MainActivity changes, if needed BroadcastReceiver
Then in the constructor of the page where you want to handle the changes, get the instance of the NotificationManager, and then subscribe to the NotificationReceived event which should get called when a user taps on the notification item.
public DashboardPage()
{
InitializeComponent();
notificationManager = DependencyService.Get<INotificationManager>();
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
Console.WriteLine("Title & Message: " + evtData.Title + evtData.Message);
};
}
You could try Plugin.LocalNotification too, it has a feature can hanle tap event, like:
public partial class App : Application
{
public App()
{
InitializeComponent();
// Local Notification tap event listener
NotificationCenter.Current.NotificationTapped += OnLocalNotificationTapped;
MainPage = new MainPage();
}
private void OnLocalNotificationTapped(NotificationEventArgs e)
{
// your code goes here
}
}
edit:
On iOS:
You can add code in DidRecieveNotiticationResponse(),like:
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
if (response.IsDefaultAction)
{
ProcessNotification(response.Notification);
}
Console.WriteLine("Called");
App.Current.MainPage=new Page();
completionHandler();
}
I'm Appy Weather's developer, and looking at giving its users the ability to show the temperature permanently on the Status Bar. Unfortunately, it doesn't appear to be straightforward, and I'm not even sure possible (though the fact other apps allow for this makes me think it must be somehow).
Please note the following:
1) the app targets Android 8.0 upwards
2) this is a Xamarin.Android app
Using TextDrawable, I've managed to dynamically create a Drawable that's converted to a Bitmap showing the current temperature that is accepted by the Notification.Builder's SetSmallIcon():
Notification.Builder builder = new Notification.Builder(context, channelId)
.SetContentText(text)
.SetOngoing(true);
var bld = Android.Ui.TextDrawable.TextDrawable.TextDrwableBuilder.BeginConfig().FontSize(72).UseFont(Typeface.Create("sans-serif-condensed", TypefaceStyle.Normal)).EndConfig();
var drawable = bld.BuildRect(title, Color.Red);
builder.SetSmallIcon(Icon.CreateWithBitmap(Helper_Icon_Droid.drawableToBitmap(drawable)));
This works:
But it isn't perfect because:
1) text size would ideally be the maximum possible in the Status Bar i.e. same as the clock's
2) width available means if the temperature is 100°+, or -10° or less, and possibly certain double digit number pairings, then it wouldn't fit and gets cut-off
3) the text will only be visible if the background's set to a colour that isn't black, white or transparent, which is not good because it's important for this to not have a solid background colour
UPDATE 1
So, as Raimo commented below, SetTicker() isn't the correct solution. Not that I've discovered it yet, but Saamer's WindowManager tip has resulted in me hopefully getting closer to the desired outcome.
I've added the following permissions to the Manifest:
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
I request permission within my Settings activity:
// permissions
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
private string[] _permissions =
{
Manifest.Permission.SystemAlertWindow
};
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE)
{
if ( Android.Provider.Settings.CanDrawOverlays(MainApplication.Context) )
{
JobManager.Instance().Scheduler().setJobTemp();
}
}
}
public void checkDrawOverlayPermission()
{
try
{
// check if we already have permission to draw over other apps
// if we don't, we need to get system permission
if ( !Android.Provider.Settings.CanDrawOverlays(MainApplication.Context) )
{
var intent = new Intent(Android.Provider.Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:" + Android.App.Application.Context.PackageName));
StartActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
// otherwise, set up the job
else
{
JobManager.Instance().Scheduler().setJobTemp();
}
}
catch (Exception ex)
{
}
}
I'm taken correctly to the phone's relevant settings permission screen, and when I grant permission and return to Settings it will run the method seen below that schedules an hourly Job (using an internal helper method I created that works fine) when the user ticks the relevant setting, as well as pushing it to the status bar directly and immediately outside the job:
public void setJobTemp()
{
try
{
if (_scheduler.GetPendingJob(Helper_Notification._NOTIFICATION_ID_TEMP) == null)
{
_jobTemp = _context.CreateJobBuilderUsingJobId<Job_Temp>(Helper_Notification._NOTIFICATION_ID_TEMP);
bool success = Helper_Job.ScheduleJob(_context, _jobTemp, 60, 5);
// besides setting the hourly job above, we want to immediately push it out too
Helper_Notification.Push( ViewModel._instance._http.Response.Hourly.Data[ViewModel._instance._http.Response.Hourly._indexStartFrom].Temperature );
}
}
catch (Exception ex)
{
}
}
This is the Resource.Layout.StatusBar_Temp resource layout file I've created to be used (using a placeholder value for the TextView for testing purposes):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/root"
style="#style/LayoutWrap">
<TextView
android:id="#+id/value"
style="#style/TextWrap"
android:text="abc"/>
</LinearLayout>
And finally, this is the method used to push out the update to the Status Bar:
public static void Push( string text )
{
IWindowManager windowManager = MainApplication.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var temp = LayoutInflater.From(MainApplication.Context).Inflate(Resource.Layout.StatusBar_Temp, null);
var layoutParams = new WindowManagerLayoutParams(1, 1, WindowManagerTypes.ApplicationOverlay, WindowManagerFlags.NotFocusable, Format.Translucent);
layoutParams.Gravity= GravityFlags.Top | GravityFlags.Left;
layoutParams.X = 0;
layoutParams.Y = 0;
windowManager.AddView(temp, layoutParams);
}
I'm using WindowManagerTypes.ApplicationOverlay because the other system types that seem to have been suggested in the past can no longer be used from 8.0 up anyway (was hitting exceptions when I tried them originally).
At the moment, with the above code, I'm not running into any exceptions, and everything appears to run smoothly both when the Job runs as well as on the initial push. However, there's a big problem: nothing appears to be drawn. For what it's worth, originally I attempted this by creating a LinearLayout containing a TextView programmatically (as opposed to using an existing layout), but that had the same issue with nothing being visible.
Any ideas?
Have you tried to use SetTicker()? So your code would look something like this (might have to use unicode 00B0 for the degree symbol)
Notification.Builder builder = new Notification.Builder(context, channelId)
.SetContentText(text)
.SetOngoing(true);
.SetTicker("17°");
There's an alternate way of figuring out the StatusBarHeight and the LayoutParams, and then adding a TextView to a LinearLayout which is then added to the WindowManager using all the information.
My app is using a NotificationListener to read out messages from various 3rd party apps, for example WhatsApp.
So far I was able to send a reply if only one chat is unread, the code is below.
However, in the case with WhatsApp, getNotification().actions returns a null object when more than two chats are unread, as the messages are bundled together. As you can see in the pictures below, if the notifications are extended there is an option to send a direct reply as well, therefore I am certain that it is possible to utilize this, also I think apps like PushBullet are using this method.
How could I access the RemoteInput of that notification?
public static ReplyIntentSender sendReply(StatusBarNotification statusBarNotification, String name) {
Notification.Action actions[] = statusBarNotification.getNotification().actions;
for (Notification.Action act : actions) {
if (act != null && act.getRemoteInputs() != null) {
if (act.title.toString().contains(name)) {
if (act.getRemoteInputs() != null)
return new ReplyIntentSender(act);
}
}
}
return null;
}
public static class ReplyIntentSender {
[...]
public final Notification.Action action;
public ReplyIntentSender(Notification.Action extractedAction) {
action = extractedAction;
[...]
}
private boolean sendNativeIntent(Context context, String message) {
for (android.app.RemoteInput rem : action.getRemoteInputs()) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putCharSequence(rem.getResultKey(), message);
android.app.RemoteInput.addResultsToIntent(action.getRemoteInputs(), intent, bundle);
try {
action.actionIntent.send(context, 0, intent);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
return false;
}
}
Some explanation how the above code works: Once a notification is received the app tries to get the actions and checks if the name is in the title of a remoteInput (normally it is in the format of "Reply to $NAME"), if that is found the Action is saved into a ReplyIntentSender class, which, when triggered by sendNativeIntent, cycles through all RemoteInputs of that Action and adds the message to the intent. If more than one chat is unread, getNotification().actions returns null.
Below are two screenshots, the first one where it is working without any problems and the second one where it doesn't.
You can consider this as my suggestion. I have done bit research on this and come up with following conclusions.(Also it looks like you have done plenty of research on this so it might be possible that you aware about what I wrote below)
Numerous apps send Wear specific notifications, and many of those contain actions accessible from an Android Wear device. We can grab those Wear notifications on the device, extracting the actions, finding the reply action (if one exists), populating it with our own response and then executing the PendingIntent which sends our response back the original app for it to send on to the recipient.
To do so you can refer this link (A nice workaround by Rob J). You can also refer this link in this context (Great research work done by Michał Tajchert).(You might need to work around with NotificationCompat.isGroupSummary)
This is what I feel(Might be I am totally wrong)
.actions method returns Array of all Notification.Action
structures attached to current notification by addAction(int,
CharSequence, PendingIntent), Here addAction method is deprecated
one so it might not working as intended.
I am not able to test this at my end otherwise I will love to provide a working solution with code.
Hope this will help you. Happy Coding!!!
When the user receives notification I would like to change/update title or body. Before showing the notification to the user. My guess is to implement this inside NotificationExtenderService.onNotificationProcessing or OneSignal.NotificationReceivedHandler
But have no idea how the API documentation is not helping either.
Solved. You can change reminder values inside onNotificationProcessing event.
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
OverrideSettings overrideSettings = new OverrideSettings();
overrideSettings.extender = new NotificationCompat.Extender() {
#Override
public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
builder.setContentText....
builder.setContentTitle...
}
}
}
You can not simply override via builder methods setContentText().
Please, check the answer on issue in OneSignal repository.
https://github.com/OneSignal/OneSignal-Android-SDK/issues/717
I'm making an accessibility service that includes notifications, I was wondering if it's possible to get the icon bitmap and "details" of the notifications, I know that the tickertext can provide the "title of the notification" but I would like to access the line that show the "details text" formerly the "getText" method of the contentview field of the notification.
It is possible to get more details and even reconstruct the Notification object. I haven't tested this, but it should work fine.
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
...
if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
Parcelable parcelable = event.getParcelableData();
if (parcelable instanceof Notification) {
// Resource id within the package context of the notifying app.
int icon = ((Notification) parcelable).icon;
Resources res = getPackageManager().getResourcesForApplication(event.getPackageName());
Drawable image = res.getDrawable(icon);
}
}
}