Android custom notification for music player Example
In this tutorial, you will learn how to create Custom Notifications in your Android music player application. A notification is a message you can display to the user in the status bar outside of your Android application. Notifications can be clicked to perform an action or to open a new activity. We will be creating a custom notification that is usually used by music player apps.
Lets get started.
Create an empty android application project and copy the following contents to MainActivity.java :
package
com.tutorialsface.customnotification;
import
android.content.Intent;
import
android.os.Bundle;
import
android.support.v7.app.ActionBarActivity;
import
android.view.Menu;
import
android.view.MenuItem;
import
android.view.View;
public
class
MainActivity
extends
ActionBarActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public
boolean
onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return
true
;
}
@Override
public
boolean
onOptionsItemSelected(MenuItem item) {
int
id = item.getItemId();
if
(id == R.id.action_settings) {
return
true
;
}
return
super
.onOptionsItemSelected(item);
}
public
void
startService(View v) {
Intent serviceIntent =
new
Intent(MainActivity.
this
, NotificationService.
class
);
serviceIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
startService(serviceIntent);
}
}
|
Change the contents of the layout file activity_main.xml:
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
"com.tutorialsface.customnotification.MainActivity"
>
<
TextView
android:id
=
"@+id/textView1"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_alignParentTop
=
"true"
android:layout_centerHorizontal
=
"true"
android:layout_marginTop
=
"53dp"
android:text
=
"Custom Notification Tutorial"
/>
<
Button
android:id
=
"@+id/button1"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_centerHorizontal
=
"true"
android:layout_centerVertical
=
"true"
android:onClick
=
"startService"
android:text
=
"Show Notification"
/>
</
RelativeLayout
>
|
In music player, playback of songs has to be done within a service which runs in background even after the application is closed. We will create such service to handle the inputs given through the buttons shown by the Notification layout.
To learn more about Android Services you can follow this.
Lets create a service now.
Create a new java file named NotificationService.java and extend it for Service as shown below:
package
com.tutorialsface.customnotification;
import
android.os.IBinder;
import
android.app.Service;
import
android.content.Intent;
public
class
NotificationService
extends
Service {
@Override
public
void
onDestroy() {
super
.onDestroy();
}
@Override
public
IBinder onBind(Intent intent) {
return
null
;
}
}
|
Override the method OnStartCommand() of Service class.
@Override
public
int
onStartCommand(Intent intent,
int
flags,
int
startId) {
if
(intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
showNotification();
Toast.makeText(
this
,
"Service Started"
, Toast.LENGTH_SHORT).show();
}
else
if
(intent.getAction().equals(Constants.ACTION.PREV_ACTION)) {
Toast.makeText(
this
,
"Clicked Previous"
, Toast.LENGTH_SHORT).show();
Log.i(LOG_TAG,
"Clicked Previous"
);
}
else
if
(intent.getAction().equals(Constants.ACTION.PLAY_ACTION)) {
Toast.makeText(
this
,
"Clicked Play"
, Toast.LENGTH_SHORT).show();
Log.i(LOG_TAG,
"Clicked Play"
);
}
else
if
(intent.getAction().equals(Constants.ACTION.NEXT_ACTION)) {
Toast.makeText(
this
,
"Clicked Next"
, Toast.LENGTH_SHORT).show();
Log.i(LOG_TAG,
"Clicked Next"
);
}
else
if
(intent.getAction().equals(
Constants.ACTION.STOPFOREGROUND_ACTION)) {
Log.i(LOG_TAG,
"Received Stop Foreground Intent"
);
Toast.makeText(
this
,
"Service Stoped"
, Toast.LENGTH_SHORT).show();
stopForeground(
true
);
stopSelf();
}
return
START_STICKY;
}
|
You will get some errors after adding the above code. So to resolve them, create a new class named Constants to store all the required constant variables.
Copy the following code for Constants.java:
package
com.tutorialsface.customnotification;
import
android.content.Context;
import
android.graphics.Bitmap;
import
android.graphics.BitmapFactory;
public
class
Constants {
public
interface
ACTION {
public
static
String MAIN_ACTION =
"com.marothiatechs.customnotification.action.main"
;
public
static
String INIT_ACTION =
"com.marothiatechs.customnotification.action.init"
;
public
static
String PREV_ACTION =
"com.marothiatechs.customnotification.action.prev"
;
public
static
aString PLAY_ACTION =
"com.marothiatechs.customnotification.action.play"
;
public
static
String NEXT_ACTION =
"com.marothiatechs.customnotification.action.next"
;
public
static
String STARTFOREGROUND_ACTION =
"com.marothiatechs.customnotification.action.startforeground"
;
public
static
String STOPFOREGROUND_ACTION =
"com.marothiatechs.customnotification.action.stopforeground"
;
}
public
interface
NOTIFICATION_ID {
public
static
int
FOREGROUND_SERVICE =
101
;
}
public
static
Bitmap getDefaultAlbumArt(Context context) {
Bitmap bm =
null
;
BitmapFactory.Options options =
new
BitmapFactory.Options();
try
{
bm = BitmapFactory.decodeResource(context.getResources(),
R.drawable.default_album_art, options);
}
catch
(Error ee) {
}
catch
(Exception e) {
}
return
bm;
}
}
|
Now create a new method inside your NotificationService.java with the name showNotification() and copy the content as shown below:
Notification status;
private
final
String LOG_TAG =
"NotificationService"
;
private
void
showNotification() {
// Using RemoteViews to bind custom layouts into Notification
RemoteViews views =
new
RemoteViews(getPackageName(),
R.layout.status_bar);
RemoteViews bigViews =
new
RemoteViews(getPackageName(),
R.layout.status_bar_expanded);
// showing default album image
views.setViewVisibility(R.id.status_bar_icon, View.VISIBLE);
views.setViewVisibility(R.id.status_bar_album_art, View.GONE);
bigViews.setImageViewBitmap(R.id.status_bar_album_art,
Constants.getDefaultAlbumArt(
this
));
Intent notificationIntent =
new
Intent(
this
, MainActivity.
class
);
notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
this
,
0
,
notificationIntent,
0
);
Intent previousIntent =
new
Intent(
this
, NotificationService.
class
);
previousIntent.setAction(Constants.ACTION.PREV_ACTION);
PendingIntent ppreviousIntent = PendingIntent.getService(
this
,
0
,
previousIntent,
0
);
Intent playIntent =
new
Intent(
this
, NotificationService.
class
);
playIntent.setAction(Constants.ACTION.PLAY_ACTION);
PendingIntent pplayIntent = PendingIntent.getService(
this
,
0
,
playIntent,
0
);
Intent nextIntent =
new
Intent(
this
, NotificationService.
class
);
nextIntent.setAction(Constants.ACTION.NEXT_ACTION);
PendingIntent pnextIntent = PendingIntent.getService(
this
,
0
,
nextIntent,
0
);
Intent closeIntent =
new
Intent(
this
, NotificationService.
class
);
closeIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
PendingIntent pcloseIntent = PendingIntent.getService(
this
,
0
,
closeIntent,
0
);
views.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);
views.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);
views.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);
views.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);
views.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);
bigViews.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);
views.setTextViewText(R.id.status_bar_track_name,
"Song Title"
);
bigViews.setTextViewText(R.id.status_bar_track_name,
"Song Title"
);
views.setTextViewText(R.id.status_bar_artist_name,
"Artist Name"
);
bigViews.setTextViewText(R.id.status_bar_artist_name,
"Artist Name"
);
bigViews.setTextViewText(R.id.status_bar_album_name,
"Album Name"
);
status =
new
Notification.Builder(
this
).build();
status.contentView = views;
status.bigContentView = bigViews;
status.flags = Notification.FLAG_ONGOING_EVENT;
status.icon = R.drawable.ic_launcher;
status.contentIntent = pendingIntent;
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, status);
}
|
You need to add the following imports for the new references in the method we just defined:
import
android.app.Notification;
import
android.app.PendingIntent;
import
android.util.Log;
import
android.view.View;
import
android.widget.RemoteViews;
import
android.widget.Toast;
|
In showNotification() method we created a new notification and added our custom layouts named status_layout.xml and status_bar_expanded.xml
status_bar.xml is the layout for small notification while status_layout_expanded.xml is for the big layout.
Leta create these two layout files and move them to layouts folder in the res directory of your project:
status_bar.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:orientation
=
"horizontal"
>
<
ImageView
android:id
=
"@+id/status_bar_album_art"
android:layout_width
=
"@dimen/status_bar_album_art"
android:layout_height
=
"@dimen/status_bar_album_art"
android:gravity
=
"center"
/>
<
ImageView
android:id
=
"@+id/status_bar_icon"
android:layout_width
=
"@dimen/status_bar_album_art"
android:layout_height
=
"@dimen/status_bar_album_art"
android:background
=
"@drawable/status_bg"
android:scaleType
=
"center"
android:src
=
"@drawable/ic_launcher"
android:visibility
=
"gone"
/>
<
LinearLayout
android:layout_width
=
"0dp"
android:layout_height
=
"wrap_content"
android:layout_gravity
=
"center_vertical"
android:layout_weight
=
"1"
android:orientation
=
"vertical"
android:paddingLeft
=
"@dimen/status_bar_button_info_container_padding_left"
>
<
TextView
android:id
=
"@+id/status_bar_track_name"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:textColor
=
"@color/white"
android:textSize
=
"@dimen/text_size_medium"
android:textStyle
=
"bold"
/>
<
TextView
android:id
=
"@+id/status_bar_artist_name"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
</
LinearLayout
>
<
ImageButton
android:id
=
"@+id/status_bar_play"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:contentDescription
=
"@string/cd_play"
/>
<
ImageButton
android:id
=
"@+id/status_bar_next"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:contentDescription
=
"@string/cd_next"
android:src
=
"@drawable/apollo_holo_dark_next"
/>
<
ImageButton
android:id
=
"@+id/status_bar_collapse"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:src
=
"@drawable/apollo_holo_dark_notifiation_bar_collapse"
/>
</
LinearLayout
>
|
status_bar_expanded.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
android:id
=
"@+id/notificationbg"
android:layout_width
=
"fill_parent"
android:layout_height
=
"128.0dip"
<
ImageView
android:id
=
"@+id/status_bar_album_art"
android:layout_width
=
"@dimen/notification_expanded_height"
android:layout_height
=
"@dimen/notification_expanded_height"
android:scaleType
=
"centerCrop"
android:layout_alignParentLeft
=
"true"
android:layout_alignParentBottom
=
"true"
/>
<
LinearLayout
android:gravity
=
"center_vertical"
android:orientation
=
"horizontal"
android:id
=
"@+id/buttons"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:divider
=
"?android:listDivider"
android:layout_toRightOf
=
"@id/status_bar_album_art"
android:layout_alignParentRight
=
"true"
android:layout_alignParentBottom
=
"true"
android:showDividers
=
"middle"
android:dividerPadding
=
"12.0dip"
>
<
ImageButton
android:id
=
"@+id/status_bar_prev"
android:background
=
"?android:selectableItemBackground"
android:padding
=
"10.0dip"
android:layout_width
=
"0.0dip"
android:layout_height
=
"@dimen/play_controls_notification"
android:src
=
"@drawable/apollo_holo_dark_prev"
android:scaleType
=
"fitCenter"
android:layout_weight
=
"1.0"
/>
<
ImageButton
android:id
=
"@+id/status_bar_play"
android:background
=
"?android:selectableItemBackground"
android:padding
=
"10.0dip"
android:layout_width
=
"0.0dip"
android:layout_height
=
"@dimen/play_controls_notification"
android:src
=
"@drawable/apollo_holo_dark_play"
android:scaleType
=
"fitCenter"
android:layout_weight
=
"1.0"
/>
<
ImageButton
android:id
=
"@+id/status_bar_next"
android:background
=
"?android:selectableItemBackground"
android:padding
=
"10.0dip"
android:layout_width
=
"0.0dip"
android:layout_height
=
"@dimen/play_controls_notification"
android:src
=
"@drawable/apollo_holo_dark_next"
android:scaleType
=
"fitCenter"
android:layout_weight
=
"1.0"
/>
</
LinearLayout
>
<
ImageView
android:background
=
"?android:dividerHorizontal"
android:layout_width
=
"wrap_content"
android:layout_height
=
"1.0px"
android:layout_toRightOf
=
"@id/status_bar_album_art"
android:layout_above
=
"@+id/buttons"
android:layout_alignParentRight
=
"true"
/>
<
ImageButton
android:id
=
"@id/status_bar_collapse"
android:background
=
"?android:selectableItemBackground"
android:padding
=
"8.0dip"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:src
=
"@drawable/apollo_holo_dark_notifiation_bar_collapse"
android:layout_alignParentTop
=
"true"
android:layout_alignParentRight
=
"true"
/>
<
LinearLayout
android:layout_gravity
=
"center_vertical"
android:orientation
=
"vertical"
android:id
=
"@+id/textarea"
android:paddingLeft
=
"@dimen/notification_padding"
android:paddingTop
=
"8.0dip"
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
android:layout_toLeftOf
=
"@id/status_bar_collapse"
android:layout_toRightOf
=
"@id/status_bar_album_art"
android:layout_alignParentTop
=
"true"
>
<
TextView
android:ellipsize
=
"marquee"
android:layout_gravity
=
"left"
android:id
=
"@+id/status_bar_track_name"
android:focusable
=
"true"
android:fadingEdge
=
"horizontal"
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
android:singleLine
=
"true"
/>
<
TextView
android:ellipsize
=
"marquee"
android:layout_gravity
=
"left"
android:id
=
"@+id/status_bar_artist_name"
android:fadingEdge
=
"horizontal"
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
android:maxLines
=
"1"
/>
<
TextView
android:ellipsize
=
"marquee"
android:layout_gravity
=
"left"
android:id
=
"@+id/status_bar_album_name"
android:fadingEdge
=
"horizontal"
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
android:maxLines
=
"1"
/>
</
LinearLayout
>
</
RelativeLayout
>
|
You will see many errors in each of the files we created but don’t worry, we will resolve all of them.
Create a folder named drawable inside /res directory of your android project and copy the following file:
status_bg.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:src
=
"@drawable/notify_panel_notification_icon_bg"
android:tileMode
=
"repeat"
/>
|
Now copy the following three files in /res/values directory in your project.
strings.xml:
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
xmlns:xliff
=
"urn:oasis:names:tc:xliff:document:1.2"
>
<!-- App name -->
<
string
name
=
"app_name"
>Music Notification</
string
>
<
string
name
=
"app_version_number"
>1.0</
string
>
<
string
name
=
"menu_settings"
>Settings</
string
>
<
string
name
=
"cd_repeat"
>Repeat one or all</
string
>
<
string
name
=
"cd_previous"
>Skip backwards</
string
>
<
string
name
=
"cd_play"
>Play and pause</
string
>
<
string
name
=
"cd_next"
>Skip forwards</
string
>
</
resources
>
|
colors.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<!-- A transparent black -->
<
color
name
=
"transparent_black"
>#aa000000</
color
>
<!-- selected tab text -->
<
color
name
=
"selected_tabtext"
>#bb000000</
color
>
<!-- non selected tab text -->
<
color
name
=
"unselected_tabtext"
>#77000000</
color
>
<
color
name
=
"shuffle_grey"
>#ffdedede</
color
>
<
color
name
=
"background_grey"
>#ffdedede</
color
>
<!-- Transparent -->
<
color
name
=
"transparent"
>#00000000</
color
>
<!-- Black -->
<
color
name
=
"black"
>#ff000000</
color
>
<!-- White -->
<
color
name
=
"white"
>#ffffffff</
color
>
</
resources
>
|
dimens.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<!-- Text sizes -->
<
dimen
name
=
"text_size_extra_micro"
>10sp</
dimen
>
<
dimen
name
=
"text_size_micro"
>12sp</
dimen
>
<
dimen
name
=
"text_size_small"
>14sp</
dimen
>
<
dimen
name
=
"text_size_medium"
>16sp</
dimen
>
<
dimen
name
=
"text_size_large"
>18sp</
dimen
>
<!-- Nofication bar button -->
<
dimen
name
=
"status_bar_button_width_height"
>48dp</
dimen
>
<
dimen
name
=
"status_bar_album_art"
>64dp</
dimen
>
<
dimen
name
=
"status_bar_button_info_container_padding_left"
>11dp</
dimen
>
<
dimen
name
=
"notification_expanded_height"
>128.0dip</
dimen
>
<
dimen
name
=
"play_controls_notification"
>48.0dip</
dimen
>
<
dimen
name
=
"notification_padding"
>8.0dip</
dimen
>
</
resources
>
|
Copy these image files into /res/drawable-hdpi folder your project.
Now modify the AndroidManifest.xml as follows:
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
package
=
"com.tutorialsface.customnotification"
android:versionCode
=
"1"
android:versionName
=
"1.0"
>
<
uses-sdk
android:minSdkVersion
=
"8"
android:targetSdkVersion
=
"21"
/>
<
application
android:allowBackup
=
"true"
android:icon
=
"@drawable/ic_launcher"
android:label
=
"@string/app_name"
android:theme
=
"@style/AppTheme"
>
<
activity
android:name
=
"com.tutorialsface.customnotification.MainActivity"
android:label
=
"@string/app_name"
>
<
intent-filter
>
<
action
android:name
=
"android.intent.action.MAIN"
/>
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
</
intent-filter
>
</
activity
>
<
service
android:name
=
"com.tutorialsface.customnotification.NotificationService"
/>
</
application
>
</
manifest
>
|
Now run the application.
The Screenshot for the final app displaying custom Notification for a Music Player: