web analytics

Android Code : ทำ Notification การแจ้งเตือนแบบพื้นฐาน

cover

บทความนี้จะบันทีกเกี่ยวกับการทำ Notification แบบให้แสดงง่ายๆครับ เนื้อหาส่วนใหญ่ก็เรียบเรียงจาก doc ของ Google อีกที
บทความนี้ไม่รวมไปถึงการ push จาก server นะ

เราจะใช้ NotificationCompat ซึ่งอยู่ใน support library v4

 

เริ่มต้น

ออกแบบ layout ก่อน โดยผมจะมีปุ่มสักปุ่มนึง กดแล้วจะให้เด้งแจ้งเตือนขึ้นมา

activity_main.xml

1

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context="com.benzneststudios.testnotification.MainActivity">
    <Button
        android:id="@+id/btn_show_notification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Show notification" />
</FrameLayout>

 

มาที่ MainActivity.java

ให้ findViewById ของปุ่มให้เรียบร้อย พร้อมกำหนด onClick ของปุ่ม

public class MainActivity extends AppCompatActivity {

    Button btnShowNotification;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnShowNotification = (Button) findViewById(R.id.btn_show_notification);
        btnShowNotification.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showNotification();
            }
        });

    }

    public void showNotification(){
        // do about noti.
    }
}

 

มาดูหน้าตา Notification ก่อน หน้าตาของ notification ก็จะต่างไปตาม andrid เวอชัน รูปข้างล่างคือของ api 21 – 23

 

2

 

พอเป็น api 24 ก็จะเปลี่ยนไป สังเกตชัดๆเลย คือย้าย ไอคอนมาด้านขวา แต่โดยหลักแล้วก็คล้ายๆของเดิม


ภาพจาก https://developer.android.com/guide/topics/ui/notifiers/notifications.html

 

เอาละ implement ส่วนของการแสดง notification กัน โดยหลักๆ คือต้อง set พวก icon แล้วก็เนื้อหา
ไอคอนเล็ก สามารถใช้จาก resource ได้ โดยมันคือ ไอคอนที่แสดงบน notification bar จะถูกแปลงเป็นสีขาวอัตโนมัติ
ส่วนไอคอนใหญ่ต้องใช้ Bitmap ก็สามารถแปลง drawble resource มาเป็น Bitmap ได้

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                R.mipmap.ic_launcher);

 

ทำการกำหนดค่าให้กับ Builder ซึ่งตัวนี้ คือตัวสำคัญของเรื่องนี้เลย
เสร็จแล้วก็เรียกคำสั่ง .build() จะได้ object ของ Notification

Notification notification =
                new NotificationCompat.Builder(context) 
                        .setSmallIcon(drawable)
                        .setLargeIcon(bitmap)
                        .setContentTitle("Benznest test notification")
                        .setContentText("Good night.")
                        .setAutoCancel(false)
                        .setColor(color)
                        .build();

การใส่ AutoCancel คือ ถ้าผู้ใช้กด notification นั้น แล้วให้ลบอันที่กดออกไป

 

กำหนดการสั่น (Vibrate)

สามารถตั้งให้สั่นได้ด้วย แถมยังตั้ง รูปแบบการสั่นได้ จะเอาจังหวะอย่างไรก็ใส่ลงไป

ก่อนอื่นเพิ่ม permission ใน AndroidManifest.xml

<uses-permission android:name="android.permission.VIBRATE" />

ใช้คำสั่ง setVibrate() โดย array of long ก็คือ จังหวะการสั่น ตัวเลขคือสั่นเป็นหน่วย ms

.setVibrate(new long[] { 500, 1000, 500 })

7

 

กำหนดความสำคัญ (Priority)

เราสามารถกำหนดความสำคัญ ของ Notification ได้โดยใช้คำสั่งนี้

.setPriority(NotificationCompat.PRIORITY_DEFAULT)

6

 

 

ส่ง Notification

ตั้งค่าไว้แล้วก็ต้องทำการส่ง notification ออกไป โดยจะทำโดยอัญเชิญ NotificationManager
แล้วเรียก notify() ก็จะส่งออกไปแล้ว โผล่ให้ user เห็น

        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(1000, notification);

 

สรุปรวมแล้ว แบบง่ายๆ

    private void showNotification() {
        Context context = MainActivity.this;
        int color = ContextCompat.getColor(context, R.color.colorPrimary);

        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                R.mipmap.ic_launcher);

        Notification notification =
                new NotificationCompat.Builder(context) 
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(bitmap)
                        .setContentTitle("Benznest test notification")
                        .setContentText("Good night.")
                        .setAutoCancel(false)
                        .setColor(color)
                        .setVibrate(new long[] { 500, 1000, 500 })
                        .build();


        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(1000, notification);
    }

 

กดปุ่มก็จะมี Notification โผล่ออกมา แต่จะยังกดไม่ได้ เพราะเรายังไม่ได้เพิ่ม Action ให้มันนั่นเอง

a1

 

ใส่เนื้อหาหลายบรรทัด

บางกรณีก็ต้องการมีข้อความเป็นลิสใน Notification

ก็สร้าง InBoxStyle ขึ้นมา แล้ว add ข้อความลงไป แทนพวก textContent ได้เลย

        NotificationCompat.InboxStyle inboxStyle =
                new NotificationCompat.InboxStyle();
        inboxStyle.setBigContentTitle("Event tracker details:");
        for (int i = 0; i < 6; i++) {
            inboxStyle.addLine("Hello "+i);
        }

 

เอา inboxStyle ใส่ลงไปใน Notification.Builder เหมือนตัวอื่นๆ

.setStyle(inboxStyle)

a4

 

สร้างหน้าที่ 2

ต่อมาก่อนจะสร้าง action มาสร้างอีกหน้ารอกันก่อน สิ่งที่จะทำคือ จะให้กดที่ Notification แล้วให้มาหน้าที่ 2 นี้
พร้อมส่งค่ามาด้วย แล้วเราก็เอามาแสดง

สร้าง activity ใหม่ให้เรียบร้อย

activity_main2.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.benzneststudios.testnotification.Main2Activity">
<TextView
    android:id="@+id/tv_answer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="36sp"
    android:text="Test"
    android:layout_gravity="center_horizontal"/>
</FrameLayout>

3

 

กลับมาที่ ActivityMain.java

เราจะใช้คำสั่ง เพิ่มเข้าไป

.setContentIntent( PendingIntent )

PendingIntent คือ intent ที่แอปอื่นสามารถเอาไปใช้ได้

โดยการทำ pendingIntent ในกรณีนี้ ก็มีขั้นตอนนิดหน่อย คือ ต้องไปสร้าง TaskStackBuilder ก่อน แล้ว add Intent ที่เราต้องการเปิดลงไป จากนั้นค่อยดึงมันออกมา ก็จะได้ PendingIntent ล่ะ

โดยขั้นตอนนี้ เราจะ put ค่าที่เตรียมจะส่งไปด้วยเลยกับ Intent ในที่นี้คือ message

    private PendingIntent getPendingIntent(Context context,String message) {
        Intent intent = new Intent(context, Main2Activity.class);
        intent.putExtra("MESSAGE", message);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        stackBuilder.addParentStack(Main2Activity.class);
        stackBuilder.addNextIntent(intent);
        return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    }

 

รวมทั้งหมด ตั้งแต่สร้าง Notification แล้ว set ค่าให้มัน เพิ่ม action แล้วส่งออกไป

 private void showNotification() {
        Context context = MainActivity.this;
        int color = ContextCompat.getColor(context, R.color.colorPrimary);
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                R.mipmap.ic_launcher);
        Notification notification =
                new NotificationCompat.Builder(context) // this is context
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(bitmap)
                        .setContentTitle("Benznest test notification")
                        .setContentText("Good night.")
                        .setAutoCancel(false)
                        .setColor(color)
                        .setContentIntent(getPendingIntent(context , "Hello World!!"))
                        .setVibrate(new long[] { 500, 1000, 500 })
                        .setLights(color, 3000, 3000)
                        .build();


        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(1000, notification);
    }

    private PendingIntent getPendingIntent(Context context,String message) {
        Intent intent = new Intent(context, Main2Activity.class);
        intent.putExtra("MESSAGE", message);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        stackBuilder.addParentStack(Main2Activity.class);
        stackBuilder.addNextIntent(intent);
        return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    }

 

มาเขียนในส่วนของการรับค่าที่ส่งมาใน หน้าที่ 2
ก็รับค่าจาก Intent แล้วใส่ให้แสดงใน textView

Main2Activity.java

public class Main2Activity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        TextView tv = (TextView) findViewById(R.id.tv_answer);
        Bundle bundle = getIntent().getExtras();
        if(bundle != null){
            tv.setText(bundle.getString("MESSAGE"));
        }
    }
}

 

ลองกดบน Notification ดู ก็จะเด้งไป activity นั้น

a2

 

 

เปิด Browser จาก notification

บางกรณีก็อาจจะอยากให้เปิด เว็บแทนการเปิด activity ใหม่ ก็สามารถทำได้เช่นกัน

เพียงเปลี่ยน pendingIntent นั่นเอง ไปใช้ Uri แทน

    private PendingIntent getPendingIntentURL(){
        String url = "http://benzneststudios.com";
        Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url));
        return PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
    }

 

แล้วใส่ใน setContentIntent เช่นเดิม

  Notification notification =
                new NotificationCompat.Builder(context) // this is context
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(bitmap)
                        .setContentTitle("Benznest test notification")
                        .setContentText("Good night.")
                        .setAutoCancel(false)
                        .setColor(color)
                        .setContentIntent(getPendingIntentURL())
                        .setVibrate(new long[] { 500, 1000, 500 })
                        .setLights(color, 3000, 3000)
                        .build();

 

ลองกด Notifiaction ก็จะไปเปิดเว็บทันที ตาม url ที่ใส่ไว้

a3

 

รู้จัก Flags

จะเห็นพารามิเตอร์นึงที่น่าสนใจ ของ getPendingIntent , getActivity มันคือ

int flags

หน้าที่ของมันก็คือกำหนด behavior ของ pendingIntent นั่นเอง

4

 

ลองกดให้มัน suggest ดู ว่ามี flags อะไรบ้าง โดยเอาเม้าไปจิ้มตรงนั้นแล้วกด (Ctrl + space bar )

5-1

FLAG_ONE_SHOT คือ ใช้ได้ครั้งเดียว ถ้าเรียกซ้ำมันจะไม่ทำงาน

FLAG_NO_CREATE คือ ไปเช็คก่อนว่า มีอยู่มัยถ้า มีอยู่แล้วมันจะไม่สร้างใหม่ พร้อมกับ return null

FLAG_CANCEL_CURRENT คือ อันก่อนหน้า ถ้ามีอยู่แล้วจะถูกยกเลิก แล้วสร้างอันใหม่

FLAG_UPDATE_CURRENT คือ ถ้ามีอยู่แล้ว จะทำการไปอัพเดท

 

การลบ Notification

ในความเป็นจริง เมื่อผู้ใช้กดดูแอปจาก notification แล้ว เราก็ควรลบมันออกจาก Notification center ซะ เดี๋ยว user จะรำคาญ

ถ้าตั้ง autoCancel ใน builder พอผู้ใช้กด มันจะลบให้อัตโนมัติ

.setAutoCancel(true)

แต่บางครั้งอาจจะมีหลาย notification ที่ต้องการลบ
คำสั่งลบทั้งหมด สามารถใช้ cancelAll() ได้เลย

NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();

 

หรือจะลบเฉพาะ notifaication ก็ได้เช่นกันโดยใช้ id

notificationManager.cancel(id);

โดย id ก็มาจากตอนที่เราใส่ไปตอน notify นั่นเอง

8

 

การอัพเดท Notification

การอัพเดท เพียงแค่เราใช้ id เดิมกับ notification ที่แสดงอยู่มันก็จะอัพเดททันที

     NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(id, notification);

 

 

Notification กับ Progress bar

เราสามารถใส่ progress bar ไปกับ notification ได้ จะเห็นได้กับพวกแอปดาวน์โหลดสิ่งต่างๆ

ก่อนอื่นก็ทำการสร้าง notification จาก Builder ตามปกติ แต่อย่าพึ่ง build() ให้เก็บใส่ตัวแปรไว้ก่อน

 final NotificationCompat.Builder notification = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(bitmap)
                .setContentTitle("Downloading")
                .setContentText("Good night.")
                .setAutoCancel(true)
                .setColor(color);

 

สร้าง thread ขึ้นมารันงานนี้ สิ่งสำคัญ คือ

การกำหนดค่า progress ให้กับ notification ถ้ามันมากกว่า 0 ตัว progressbar จะโผล่ออกมาอัตโนมัติ

notification.setProgress(100, i, false);

พอกำหนดเสร็จแล้ว อย่าลืม notify

notificationManager.notify(0, notification.build());

 

หลังจากเสร็จงานแล้ว อย่าลืม แสดงข้อความผลลัพธ์ด้วยเช่น ดาวน์โหลดเสร็จแล้วนะ
โดยจะใช้การอัพเดทอันเก่า หรือจะลบตัว progress แล้วใส่ใหม่ก็ได้เช่นกัน

      new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i;
                        for (i = 0; i <= 100; i += 5) {
                            notification.setProgress(100, i, false);
                            notificationManager.notify(0, notification.build());
                            try {
                                // do something.
                                Thread.sleep(100); // Sleep 0.1 s
                            }
                            catch (InterruptedException e) {
                            }
                        }
                        notificationManager.cancel(0); // remove notification progress.

                        notification.setContentText("Download complete")
                                // Removes the progress bar
                                .setProgress(0, 0, false);
                        notificationManager.notify(1000, notification.build());
                    }
                }
        ).start();

 

ลองรัน กดปุ่มแล้ว progressbar ก็จะทำงาน

a5

 

ปุ่ม Action ใน notification

ใน notification นอกจากจะให้ user กดโดยตรงแล้ว เราสามารถเพิ่มปุ่ม action อื่นๆได้อีกด้วย เช่น แชร์เลย ตอบกลับทันที
โดยฟีเจอร์นี้รองรับเฉพาะ api 20 ขึ้นไป

วิธีการคือ ใส่เพิ่มใน builder เช่นเคยโดยใช้คำสั่ง addAction

.addAction(Action);

 

การสร้าง Action สามารถกำหนด icon ได้ และกำหนดคำที่แสดง

 NotificationCompat.Action action =
                new NotificationCompat.Action.Builder(R.mipmap.ic_launcher,"OK", getPendingIntent(context,"Hello World"))
                        .build();

9

 

การใส่ Heads up Notification

มันคือ การเด้ง notification ขึ้นมาบนจอ ขณะผู้ใช้ ใช้งานอยู่ ใช้ได้กับ api 21 ขึ้นไปนะ (Lolipop)

ก่อนอื่นปรับให้ activity แสดงแบบ Fullscreen

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);
        ..
        ..
    }

 

ที่ตัว Builder กำหนด vibrate กับ Priority ให้ High หรือ Max

NotificationCompat.Builder(context)
                ..
                ..
                .setVibrate(new long[]{50,50})
                .setPriority(NotificationCompat.PRIORITY_MAX)
                ..
                ..

a6

การกำหนด Visibility ของ Notification

ที่ Builder ก็ให้ใช้คำสั่ง setVisibility() มี Visibility ให้เลือก 3 ตัว

10

VISIBILITY_PUBLIC แสดงตามปกติ ทั้ง notification center , lock screen

VISIBILITY_SECRET ไม่แสดงบน lock screen

VISIBILITY_PRIVATE แสดงแต่หัวข้อและไอคอน ไม่แสดงเนื้อหา

 

 

Notification แบบใส่รูปภาพ

ใส่รูปภาพใน Notification ได้ด้วย เพิ่มความน่าสนใจให้กับ notification
ตรงนี้มีนิดนึง คือ

ถ้าใช้แบบแสดงรูป .setContentText() จะใช้ไม่ได้ ต้องไปใส่ body ใน
notiStyle.setSummaryText(body); ของ style แทนนะ

NotificationCompat.BigPictureStyle notiStyle = new NotificationCompat.BigPictureStyle();
notiStyle.setSummaryText(body);
        Bitmap picture = null;
        try {
            picture = BitmapFactory.decodeStream((InputStream) new URL(url_image).getContent());
            if (picture != null) {
                notiStyle.bigPicture(picture );
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(body)
                .setAutoCancel(true)
                .setContentIntent(pendingIntent)
                .setStyle(notiStyle);

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(100, notificationBuilder.build());

11

 

การกำหนดให้ป้องกันการลบ

กรณีที่อยากให้ Notification นั้นคงอยู่ ลบไม่ได้ เราสามารถก็กำหนด Flag ให้ได้ แม้ว่าจะ Clear ก็จะไม่ถูกลบ
แต่ถ้าผู้ใช้ลบแอป ก็จะหายไป

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(body)
                .setAutoCancel(false)
                .setContentIntent(pendingIntent);

        Notification notification = notificationBuilder.build();
        notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
        
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(100, notification);

a7

 

 

Source code

https://gist.github.com/benznest/331db4b521d700c5b77664b894a8f971

จบแล้ว

ลองเอาไปใช้งานดูครับ

(:

 

Reference

https://developer.android.com/guide/topics/ui/notifiers/notifications.html
http://devahoy.com/posts/android-notification/