Android Build your own Custom Notification
Hello everyone, in this article we are going to talk about creating a custom notification. You can use these notifications for your music player application or another type of projects. I will make examples with my own music player application.Now Let's get started.
First we are are going to use a service because we are going to play our musics from background. We will start our notification with startForeground. We need two design files: one of them is for the big view, the other one is for small view.
widget_big.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="100dp" >
<ImageView
android:id="@+id/imgWidgetAlbumArt"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher"
/>
<TextView
android:id="@+id/lblWidgetCurrentMusicName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/imgWidgetAlbumArt"
android:layout_alignParentTop="true"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="2dp"
android:textColor="@color/ContentBack"
android:textSize="16sp"
android:singleLine="true"
tools:text="@string/app_name"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
/>
<TextView
android:id="@+id/lblWidgetCurrentArtistName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/lblWidgetCurrentMusicName"
android:layout_below="@id/lblWidgetCurrentMusicName"
android:layout_marginBottom="8dp"
android:textColor="#AB000000"
android:textSize="14sp"
android:singleLine="true"
tools:text="@string/app_name"
/>
<RelativeLayout
android:id="@+id/layout_controls"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_toEndOf="@+id/imgWidgetAlbumArt"
android:layout_alignParentBottom="true">
<FrameLayout
android:id="@+id/frame_playPrev"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:clickable="true"
android:layout_alignParentStart="true"
>
<ImageButton
android:id="@+id/btnWidgetPlayPrevious"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/vector_previous"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/frame_playPause"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:clickable="true"
android:layout_toEndOf="@id/frame_playPrev" >
<ImageButton
android:id="@+id/btnWidgetPlayPauseMusic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/vector_play"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/frame_playNext"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/frame_playPause"
android:clickable="true" >
<ImageButton
android:id="@+id/btnWidgetPlayNext"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/vector_next"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/frame_close"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:clickable="true" >
<ImageButton
android:id="@+id/btnWidgetCloseService"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/vector_close"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
</RelativeLayout>
</RelativeLayout>
And my Small widget design file is below:
widget_small.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="60dp"
tools:layout_height="60dp"
>
<ImageView
android:id="@+id/imgWidgetAlbumArt"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher"
/>
<FrameLayout
android:id="@+id/frame_close"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_alignParentEnd="true"
android:visibility="gone"
>
<ImageView
android:id="@+id/btnWidgetCloseService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/vector_close"
android:layout_gravity="center"
/>
</FrameLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/imgWidgetAlbumArt"
android:layout_toStartOf="@+id/layout_controls"
android:layout_centerVertical="true"
>
<TextView
android:id="@+id/lblWidgetCurrentMusicName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:singleLine="true"
tools:text="@string/app_name"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
/>
<TextView
android:id="@+id/lblWidgetCurrentArtistName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:singleLine="true"
tools:text="@string/app_name"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_controls"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
>
<FrameLayout
android:id="@+id/button_play_last"
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
>
<ImageButton
android:id="@+id/btnWidgetPlayPrevious"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:src="@drawable/vector_previous"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/button_play_pause"
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
>
<ImageButton
android:id="@+id/btnWidgetPlayPauseMusic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:src="@drawable/vector_play"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/button_play_next"
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
>
<ImageButton
android:id="@+id/btnWidgetPlayNext"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:src="@drawable/vector_next"
android:layout_gravity="center"
android:background="@null"
/>
</FrameLayout>
</LinearLayout>
</RelativeLayout>
Now our design file is ready and we will prepare our service to show notifications.
In here we have to set up our intents to make some operations via notification clicks. For example in here we are going to play or pause music, also play next or previous music. And also we will close the application via these intents. Also We will set up our current music inside notification.
At begining of the code I recommend you to define below code variables for using it with Intents
private static final String ACTION_PLAY_PAUSE = "com.thecodeprogram.themusic.ACTION.PLAY_PAUSE";
private static final String ACTION_PLAY_LAST = "com.thecodeprogram.themusic.ACTION.PLAY_LAST";
private static final String ACTION_PLAY_NEXT = "com.thecodeprogram.themusic.ACTION.PLAY_NEXT";
private static final String ACTION_STOP_SERVICE = "com.thecodeprogram.themusic.ACTION.STOP_SERVICE";
private static final String NOTIFICATION_CHANNEL_ID = "com.thecodeprogram.themusic";
private static final String channelName = "TheMusic Background Service";
Below code block will show these operations.
First this code block will update the music informations. I will update the Remoteview via this function.
private void updateRemoteViews(RemoteViews remoteView) {
if (currentMusic != null) {
remoteView.setTextViewText(R.id.lblWidgetCurrentMusicName, "Music name");
remoteView.setTextViewText(R.id.lblWidgetCurrentArtistName, "Music Artist");
}
remoteView.setImageViewResource(R.id.btnWidgetPlayPauseMusic, isMusicPlaying == true ? R.drawable.vector_pause : R.drawable.vector_play);
if(theMusicApplication.global_programIsReady)
{
Bitmap album = (Bitmap)your_image;
if (album == null) {
remoteView.setImageViewResource(R.id.imgWidgetAlbumArt, R.mipmap.ic_launcher);
} else {
remoteView.setImageViewBitmap(R.id.imgWidgetAlbumArt, album);
}
}
}
Below code block will set the intents for operations of notification. I will set remoteviews via below function. When I created the notification I will call these methods for configuring.
private void setUpRemoteView(RemoteViews remoteView) {
Intent closeIntent = new Intent(mainInstance, MusicPlayerService.class);
closeIntent.setAction(ACTION_STOP_SERVICE);
PendingIntent pcloseIntent = PendingIntent.getService(mainInstance, 0, closeIntent, 0);
Intent playNextIntent = new Intent(mainInstance, MusicPlayerService.class);
playNextIntent.setAction(ACTION_PLAY_NEXT);
PendingIntent pNextIntent = PendingIntent.getService(mainInstance, 0, playNextIntent, 0);
Intent playPrevIntent = new Intent(mainInstance, MusicPlayerService.class);
playPrevIntent.setAction(ACTION_PLAY_LAST);
PendingIntent pPrevIntent = PendingIntent.getService(mainInstance, 0, playPrevIntent, 0);
Intent playPauseIntent = new Intent(mainInstance, MusicPlayerService.class);
playPauseIntent.setAction(ACTION_PLAY_PAUSE);
PendingIntent pplayPauseIntent = PendingIntent.getService(mainInstance, 0, playPauseIntent, 0);
remoteView.setImageViewResource(R.id.btnWidgetCloseService, R.drawable.vector_close);
remoteView.setImageViewResource(R.id.btnWidgetPlayPrevious, R.drawable.vector_previous);
remoteView.setImageViewResource(R.id.btnWidgetPlayNext, R.drawable.vector_next);
remoteView.setOnClickPendingIntent(R.id.btnWidgetCloseService, pcloseIntent);
remoteView.setOnClickPendingIntent(R.id.btnWidgetPlayPrevious, pPrevIntent);
remoteView.setOnClickPendingIntent(R.id.btnWidgetPlayNext, pNextIntent);
remoteView.setOnClickPendingIntent(R.id.btnWidgetPlayPauseMusic, pplayPauseIntent);
}
Above methods are ready, When I created the notifications, I will create the layouts with below methods.
private RemoteViews getSmallContentView() {
if (mContentViewSmall == null) {
mContentViewSmall = new RemoteViews(getPackageName(), R.layout.widget_small);
setUpRemoteView(mContentViewSmall);
}
updateRemoteViews(mContentViewSmall);
return mContentViewSmall;
}
private RemoteViews getBigContentView() {
if (mContentViewBig == null) {
mContentViewBig = new RemoteViews(getPackageName(), R.layout.widget_big);
setUpRemoteView(mContentViewBig);
}
updateRemoteViews(mContentViewBig);
return mContentViewBig;
}
In this case our methods which will prepare our notification widgets are ready. At last we can show them while normal operations.
Here we need to be careful about target API level. I prefer write two methods for under API26 and upper API 26. As you can see below code block I check the API level and I start the show notification function for different API levels.
We use setCustomContentView and setCustomBigContentView functions which we defined above.
private void sendNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startNotification_afterAPI26();
}else{
startNotification_underAPI26();
}
}
private void startNotification_underAPI26() {
Intent notificationIntent = new Intent(mainInstance, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT );
PendingIntent pendingIntent = PendingIntent.getActivity(mainInstance, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(mainInstance, NOTIFICATION_CHANNEL_ID)
.setCustomContentView(getSmallContentView())
.setCustomBigContentView(getBigContentView())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setPriority(Notification.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build();
startForeground(1, notification);
}
@RequiresApi(Build.VERSION_CODES.O)
private void startNotification_afterAPI26() {
Intent notificationIntent = new Intent(mainInstance, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_FROM_BACKGROUND );
PendingIntent pendingIntent = PendingIntent.getActivity(mainInstance, 0,
notificationIntent, 0);
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mainInstance, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setPriority(NotificationManager.IMPORTANCE_LOW)
.setCategory(Notification.EXTRA_MEDIA_SESSION)
.setCustomContentView(getSmallContentView())
.setCustomBigContentView(getBigContentView())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setPriority(Notification.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build();
startForeground(1, notification);
}
At last we are going to create and listen intents for notifications. We will run it at onStartCommand function of the service.
Below code block you can see What I talked about.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getAction();
if (action != null) {
Log.d("get_action", action);
switch (action)
{
case ACTION_PLAY_PAUSE:
fnc_playPauseMusic();
break;
case ACTION_PLAY_NEXT:
this.fnc_goNextMusic();
break;
case ACTION_PLAY_LAST:
fnc_goPreviousMusic();
break;
case ACTION_STOP_SERVICE:
Log.d("close_program", "close_program");
fnc_closeProgram();
break;
}
}
sendNotification();
return START_STICKY;
}
That is all in this article.
Have a great custom Notificating.
Burak Hamdi TUFAN
Comments
I am very happy, it works very well! thanks, help me a lot.
2021/05/25 12:52:32You are very welcome :)
2021/05/25 14:43:35thank you for much it work
2023/04/14 18:30:32