Android Build your own Custom Notification

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.

First I want to share my notification design files.
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>

This screen will be like this.

Android Big Notification

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>

This screen will be shown like below image:

Android Small Notification

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


Tags


Share this Post

Send with Whatsapp

Post a Comment

Success! Your comment sent to post. It will be showed after confirmation.
Error! There was an error sending your comment. Check your inputs!

Comments

  • Thecodeprogram User
    MrDario

    I am very happy, it works very well! thanks, help me a lot.

    2021/05/25 12:52:32
    • Thecodeprogram User
      Burak Hamdi TUFAN

      You are very welcome :)

      2021/05/25 14:43:35
  • Thecodeprogram User
    Mahmoud

    thank you for much it work

    2023/04/14 18:30:32