কম্পিউটার টিউটোরিয়াল

Android-এ এক্সপাইয়ারি ডেটসহ নোটিফিকেশন তৈরি করার পদ্ধতি

নির্দিষ্ট সময়ে নোটিফিকেশন শিডিউল করা (Expiration Date)

এই টিউটোরিয়ালে দেখানো হয়েছে কীভাবে ব্যবহারকারী একটি তারিখ বেছে নেয়, এবং সেই সময়ে আটোমেটিক্যালি একটি নোটিফিকেশন প্রদর্শিত হয়। আমরা AlarmManager, DatePickerDialog, এবং BroadcastReceiver ব্যবহার করব।

কৌশল

  1. ব্যবহারকারী DatePicker থেকে তারিখ বেছে নেয়
  2. সিলেক্ট করা তারিখের মিলিসেকেন্ড ক্যালকুলেট করুন
  3. AlarmManager.setExact() দিয়ে BroadcastReceiver ট্রিগার শিডিউল করুন
  4. Receiver-এ নোটিফিকেশন বিল্ড করে দেখান

ধাপ ১: প্রজেক্ট সেটআপ

Package: com.example.schedulednotification

ধাপ ২: লেআউট (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvDate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:hint="তারিখ নির্বাচন করতে ট্যাপ করুন"
        android:onClick="showDatePicker"
        android:padding="16dp"
        android:textSize="18sp" />

</RelativeLayout>

ধাপ ৩: MainActivity.java

package com.example.schedulednotification;

import android.app.AlarmManager;
import android.app.DatePickerDialog;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    public static final String CHANNEL_ID = "scheduled_notif_channel";
    private static final String DEFAULT_CHANNEL = "default";

    private TextView tvDate;
    private final Calendar selectedCalendar = Calendar.getInstance();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvDate = findViewById(R.id.tvDate);
    }

    public void showDatePicker(View view) {
        new DatePickerDialog(this, dateSetListener,
                selectedCalendar.get(Calendar.YEAR),
                selectedCalendar.get(Calendar.MONTH),
                selectedCalendar.get(Calendar.DAY_OF_MONTH)).show();
    }

    private final DatePickerDialog.OnDateSetListener dateSetListener = 
            (view, year, month, dayOfMonth) -> {
        selectedCalendar.set(Calendar.YEAR, year);
        selectedCalendar.set(Calendar.MONTH, month);
        selectedCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        updateLabel();
    };

    private void updateLabel() {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
        Date date = selectedCalendar.getTime();
        tvDate.setText(sdf.format(date));
        
        // নোটিফিকেশন শিডিউল করুন
        scheduleNotification(getNotification("শিডিউল করা নোটিফিকেশন: " + sdf.format(date)), 
                           date.getTime());
    }

    private Notification getNotification(String content) {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, DEFAULT_CHANNEL)
                .setContentTitle("শিডিউল্ড নোটিফিকেশন")
                .setContentText(content)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setAutoCancel(true)
                .setChannelId(CHANNEL_ID);
        return builder.build();
    }

    private void scheduleNotification(Notification notification, long triggerTimeMillis) {
        Intent intent = new Intent(this, NotificationPublisher.class);
        intent.putExtra(NotificationPublisher.EXTRA_NOTIFICATION_ID, 1);
        intent.putExtra(NotificationPublisher.EXTRA_NOTIFICATION, notification);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                this, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        
        // Trigger time = selected date time in milliseconds
        // AlarmManager.RTC_WAKEUP = Real Time Clock (wall clock) + wake up device
        if (alarmManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(
                        AlarmManager.RTC_WAKEUP, triggerTimeMillis, pendingIntent);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTimeMillis, pendingIntent);
            } else {
                alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTimeMillis, pendingIntent);
            }
        }
    }
}

ধাপ ৪: NotificationPublisher.java (BroadcastReceiver)

package com.example.schedulednotification;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class NotificationPublisher extends BroadcastReceiver {

    public static final String EXTRA_NOTIFICATION_ID = "notification-id";
    public static final String EXTRA_NOTIFICATION = "notification";

    @Override
    public void onReceive(Context context, Intent intent) {
        NotificationManager manager = (NotificationManager) 
                context.getSystemService(Context.NOTIFICATION_SERVICE);
        
        Notification notification = intent.getParcelableExtra(EXTRA_NOTIFICATION);
        int id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 0);

        if (notification == null) return;

        // চ্যানেল নিশ্চিত করুন (Receiver-এও লাগে)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    MainActivity.CHANNEL_ID, "শিডিউল্ড নোটিফিকেশন", 
                    NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel);
        }

        manager.notify(id, notification);
    }
}

ধাপ ৫: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.schedulednotification">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ScheduledNotification">

        <receiver
            android:name=".NotificationPublisher"
            android:enabled="true"
            android:exported="true" />

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

গুরুত্বপূর্ণ: Android 12+ (API 31+) Exact Alarm Permission

Android 12 থেকে `setExact()`, `setExactAndAllowWhileIdle()` ব্যবহারে SCHEDULE_EXACT_ALARM পারমিশন লাগে। রানটাইমে চেক করুন:

// MainActivity-এ onCreate বা showDatePicker-এ
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    if (alarmManager != null && !alarmManager.canScheduleExactAlarms()) {
        Intent intent = new Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
        startActivity(intent);
        return;
    }
}

ব্যবহারকারীর জন্য টাইম পিকারের সংযোজন (বোনাস)

শুধু তারিখ নয়, সময়ও বেছে নিতে চাইলে `TimePickerDialog` যোগ করুন:

// MainActivity-এ field যোগ করুন
private int selectedHour = 9, selectedMinute = 0;

// DatePicker এর পর TimePicker দেখান
private void showTimePicker() {
    new TimePickerDialog(this, (view, hour, minute) -> {
        selectedHour = hour;
        selectedMinute = minute;
        selectedCalendar.set(Calendar.HOUR_OF_DAY, hour);
        selectedCalendar.set(Calendar.MINUTE, minute);
        updateLabel();
    }, selectedHour, selectedMinute, true).show();
}

পরীক্ষা পদ্ধতি

  1. অ্যাপ চালু করুন → "তারিখ নির্বাচন করতে ট্যাপ করুন" চাপুন
  2. একটি ভবিষ্যৎ তারিখ (এবং ঐচ্ছিক সময়) বেছে নিন
  3. নির্দিষ্ট সময়ের জন্য অপেক্ষা করুন (অথবা ১-২ মিনিট পরে সেট করে টেস্ট করুন)
  4. নোটিফিকেশন প্যানেলে "শিডিউল্ড নোটিফিকেশন" দেখুন

সাধারণ সমস্যা ও সমাধান

সমস্যাসমাধান
নির্দিষ্ট সময়ে নোটিফিকেশন আসে নাExact alarm permission granted কিনা চেক করুন, Doze মোডে setExactAndAllowWhileIdle ব্যবহার করুন
Receiver কল হয় নাManifest-এ receiver exported=true, class name সঠিক কিনা চেক করুন
Parcelable crashNotification পাস করার সময় putExtra/getParcelableExtra সঠিক কী ব্যবহার করুন

আধুনিক বিকল্প: WorkManager (সুপারিশ করা)

// OneTimeWorkRequest with setInitialDelay
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(NotifyWorker.class)
        .setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
        .build();
WorkManager.getInstance(context).enqueue(work);

WorkManager দোগ মোড, অ্যাপ স্ট্যান্ডবাই, বুট কমপ্লিট সব হ্যান্ডেল করে।

  1. Android-এ রিমাইন্ডার নোটিফিকেশন তৈরি করার পদ্ধতি

  2. অ্যান্ড্রয়েডে রাউন্ডেড কর্নার সহ ListView তৈরি করার পদ্ধতি

  3. অ্যান্ড্রয়েডে গোলাকার কোণাযুক্ত EditText তৈরি করার পদ্ধতি

  4. CSS দিয়ে নোটিফিকেশন বাটন তৈরি করার পদ্ধতি